diff --git a/CHANGES.md b/CHANGES.md index 99fd582..218a57a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,94 @@ CHANGES ======= +unreleased +---------- +* Avoid NullPointerException in isEnabled method. See #53, #56. Great thanks to atcarmo for this patch. +* Updated selenium-server dependency to 2.43.1. See #58. Great thanks to WojtekKowaluk for this patch. + * Corrected Google Search word + * Fixed JSEvents test + * Added Selendroid + * Added Appium +* Allow subclassing of Selenium2Library by moving it to a package. + +1.4.0.6 +------- +* Fixed 'Select Window' always selecting the last window and not the desired window. + +1.4.0.5 +------- +* Fixed the use of == to compare Strings in FormElement. See #49 +* Fixed 'Get Alert Message'. See #50 + * 'Get Alert Message' does not confirm the Alert any more. 'Get Alert Message' + Confirm = 'Confirm Action'. + * Added keyword 'Choose Cancel On Confirmation' + * Added keyword 'Choose Ok On Confirmation' +* Fixed 'Select Window' failing, when the current window is already closed. See #48 + +1.4.0.4 +------- +* Update xml-doclet to 1.0.4 +* Make selenium2library-java 1.6 compliant. See #44 + +1.4.0.3 +------- +* Update robotframework to 2.8.3 +* Fixed a UnicodeDecodeError at logging strings containing backslashes. + +1.4.0.2 +------- + +* Update selenium to 2.39.0 +* Update phantomjsdriver to 1.1.0 +* DesiredCapabilities can be specified as complex JSON strings now +* Removed the ffProfileDir argument from Open Browser +* Added browserOptions argument to Open Browser + +1.4.0.1 +------- + +* Fixed wrong separator in Javadoc2Libdoc. See #40 +* Fixed Page Should Contain Button fails to find buttons with the button tag. See #43 + +1.4.0.0 +------- + +* Porting recent changes in the master branch of the Python Selenium2Library + from id b4a3e500 until cf971d91 to this Java port. This contains roughly: + * Added 'Get Window Size' and 'Set Window Size' keywords matching the Selenium functionality. + * Added new keyword 'Click Element At Coordinates'. + * Added keywords for verifying text entered into textarea elements. + * 'Textarea Should Contain' + * 'Textarea Should Not Contain' + * 'Textarea Value Should Be' + * 'Textarea Value Should Not Be' + * 'Mouse Up' doesn't click any more on the element. + * Raise exception in selecting non-existing item in list. Error handling varies + between single-select and multi-select lists. See keyword documentation for + more information. +* Back-port recent changes from version 1.3 and 1.4 of Python library. See #35 +* Jump Version number to 1.4.0.0 to reflect the new version of the Python library. + +1.2.0.14 +-------- + +* Fixed an ArrayIndexOutOfBoundsException in the Select Window keyword. See #27 +* Added the possibility to set logging directory. See #28. +* Refactoring of the library to a JavaLibCore AnnotationLibrary. See #28. +* Added keyword documentation from library for e.g. Ride. See #16, #28. +* Added keyword missing documentation for new keywords. See #32. +* Added access to the current WebDriver instance from custom libraries. #30 +* Generating libdoc from javadoc. + +1.2.0.13 +-------- + +* Fixed a NullPointerException in Capture Page Screenshot when Log File is set to NONE. See #24 +* Fixed that library can't be instrumented with JaCoCo. See #22 +* Update robotframework to 2.8.1 +* Update robotframework-maven-plugin to 1.2 +* Update aspectj to 1.7.3 +* Update java.version to 1.7 + 1.2.0.12 -------- diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index ed99bbc..4ab7593 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -1,24 +1,26 @@ [INFO] ------------------------------------------------------------------------ - [INFO] Building Robot Framework :: Selenium2Library 1.2.0.9-SNAPSHOT + [INFO] Building Robot Framework :: Selenium2Library 1.2.0.14-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ robotframework-selenium2library-java --- - [INFO] com.github.markusbernhardt:robotframework-selenium2library-java:jar:1.2.0.9-SNAPSHOT - [INFO] +- org.seleniumhq.selenium:selenium-server:jar:2.33.0:compile + [INFO] com.github.markusbernhardt:robotframework-selenium2library-java:jar:1.2.0.14-SNAPSHOT + [INFO] +- org.robotframework:javalib-core:jar:1.2:compile + [INFO] | +- commons-collections:commons-collections:jar:3.2:compile + [INFO] | \- com.thoughtworks.paranamer:paranamer:jar:1.1.2:compile + [INFO] +- org.seleniumhq.selenium:selenium-server:jar:2.34.0:compile [INFO] | +- org.bouncycastle:bcprov-jdk15on:jar:1.48:compile [INFO] | +- org.bouncycastle:bcpkix-jdk15on:jar:1.48:compile [INFO] | +- mx4j:mx4j-tools:jar:3.0.1:compile [INFO] | +- org.mortbay.jetty:servlet-api-2.5:jar:6.1.9:compile [INFO] | +- org.seleniumhq.selenium:jetty-repacked:jar:7.6.1:compile [INFO] | +- net.jcip:jcip-annotations:jar:1.0:compile - [INFO] | +- org.seleniumhq.selenium:selenium-java:jar:2.33.0:compile - [INFO] | | +- org.seleniumhq.selenium:selenium-android-driver:jar:2.33.0:compile - [INFO] | | +- org.seleniumhq.selenium:selenium-chrome-driver:jar:2.33.0:compile - [INFO] | | +- org.seleniumhq.selenium:selenium-htmlunit-driver:jar:2.33.0:compile + [INFO] | +- org.seleniumhq.selenium:selenium-java:jar:2.34.0:compile + [INFO] | | +- org.seleniumhq.selenium:selenium-android-driver:jar:2.34.0:compile + [INFO] | | +- org.seleniumhq.selenium:selenium-chrome-driver:jar:2.34.0:compile + [INFO] | | +- org.seleniumhq.selenium:selenium-htmlunit-driver:jar:2.34.0:compile [INFO] | | | \- net.sourceforge.htmlunit:htmlunit:jar:2.12:compile [INFO] | | | +- xalan:xalan:jar:2.7.1:compile [INFO] | | | | \- xalan:serializer:jar:2.7.1:compile - [INFO] | | | +- commons-collections:commons-collections:jar:3.2.1:compile [INFO] | | | +- org.apache.commons:commons-lang3:jar:3.1:compile [INFO] | | | +- org.apache.httpcomponents:httpmime:jar:4.2.3:compile [INFO] | | | +- net.sourceforge.htmlunit:htmlunit-core-js:jar:2.12:compile @@ -31,39 +33,39 @@ [INFO] | | | +- org.eclipse.jetty:jetty-util:jar:8.1.9.v20130131:compile [INFO] | | | +- org.eclipse.jetty:jetty-io:jar:8.1.9.v20130131:compile [INFO] | | | \- org.eclipse.jetty:jetty-http:jar:8.1.9.v20130131:compile - [INFO] | | +- org.seleniumhq.selenium:selenium-firefox-driver:jar:2.33.0:compile + [INFO] | | +- org.seleniumhq.selenium:selenium-firefox-driver:jar:2.34.0:compile [INFO] | | | \- commons-io:commons-io:jar:2.2:compile - [INFO] | | +- org.seleniumhq.selenium:selenium-ie-driver:jar:2.33.0:compile - [INFO] | | +- org.seleniumhq.selenium:selenium-iphone-driver:jar:2.33.0:compile - [INFO] | | +- org.seleniumhq.selenium:selenium-safari-driver:jar:2.33.0:compile - [INFO] | | +- org.seleniumhq.selenium:selenium-support:jar:2.33.0:compile + [INFO] | | +- org.seleniumhq.selenium:selenium-ie-driver:jar:2.34.0:compile + [INFO] | | +- org.seleniumhq.selenium:selenium-iphone-driver:jar:2.34.0:compile + [INFO] | | +- org.seleniumhq.selenium:selenium-safari-driver:jar:2.34.0:compile + [INFO] | | +- org.seleniumhq.selenium:selenium-support:jar:2.34.0:compile [INFO] | | \- org.webbitserver:webbit:jar:0.4.14:compile [INFO] | | \- io.netty:netty:jar:3.5.2.Final:compile [INFO] | +- org.yaml:snakeyaml:jar:1.8:compile [INFO] | \- commons-codec:commons-codec:jar:1.6:compile - [INFO] +- com.opera:operadriver:jar:1.1:compile + [INFO] +- com.opera:operadriver:jar:1.5:compile [INFO] | +- com.opera:operalaunchers:jar:1.1:compile [INFO] | +- com.google.protobuf:protobuf-java:jar:2.4.1:compile - [INFO] | +- com.google.guava:guava:jar:13.0.1:compile + [INFO] | +- com.google.guava:guava:jar:14.0:compile [INFO] | +- commons-jxpath:commons-jxpath:jar:1.3:compile [INFO] | +- org.apache.commons:commons-exec:jar:1.1:compile [INFO] | \- org.ini4j:ini4j:jar:0.5.2:compile - [INFO] +- com.github.detro.ghostdriver:phantomjsdriver:jar:1.0.3:compile - [INFO] | \- org.seleniumhq.selenium:selenium-remote-driver:jar:2.31.0:compile + [INFO] +- com.github.detro.ghostdriver:phantomjsdriver:jar:1.0.4:compile + [INFO] | \- org.seleniumhq.selenium:selenium-remote-driver:jar:2.33.0:compile [INFO] | +- cglib:cglib-nodep:jar:2.1_3:compile [INFO] | +- org.json:json:jar:20080701:compile - [INFO] | +- org.seleniumhq.selenium:selenium-api:jar:2.31.0:compile + [INFO] | +- org.seleniumhq.selenium:selenium-api:jar:2.33.0:compile [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.2.1:compile [INFO] | | +- org.apache.httpcomponents:httpcore:jar:4.2.1:compile [INFO] | | \- commons-logging:commons-logging:jar:1.1.1:compile [INFO] | +- net.java.dev.jna:jna:jar:3.4.0:compile [INFO] | \- net.java.dev.jna:platform:jar:3.4.0:compile - [INFO] +- org.robotframework:robotframework:jar:2.8:compile - [INFO] \- org.aspectj:aspectjrt:jar:1.6.11:compile + [INFO] +- org.robotframework:robotframework:jar:2.8.1:compile + [INFO] \- org.aspectj:aspectjrt:jar:1.7.3:compile [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ - [INFO] Total time: 4.482s - [INFO] Finished at: Wed Jun 12 08:12:03 CEST 2013 - [INFO] Final Memory: 11M/81M + [INFO] Total time: 3.454s + [INFO] Finished at: Sat Oct 12 11:58:25 CEST 2013 + [INFO] Final Memory: 14M/213M [INFO] ------------------------------------------------------------------------ \ No newline at end of file diff --git a/README.md b/README.md index 27adf1d..2d01a7a 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,18 @@ Java port of the Selenium 2 (WebDriver) Python library for Robot Framework Introduction ------------ +Selenium2Library is a web testing library for Robot Framework that leverages +the [Selenium 2 (WebDriver)](http://docs.seleniumhq.org/docs/03_webdriver.jsp) +libraries from the [Selenium](http://docs.seleniumhq.org) project. +It is modeled after (and forked from) the Robot Framework +[SeleniumLibrary](http://code.google.com/p/robotframework-seleniumlibrary/) +library, but re-implemented to use Selenium 2 and WebDriver technologies. +* More information about this library can be found in the + [Keyword Documentation](http://search.maven.org/remotecontent?filepath=com/github/markusbernhardt/robotframework-selenium2library-java/1.4.0.6/robotframework-selenium2library-java-1.4.0.6-libdoc.html). +* For keyword completion in RIDE you can download this + [Library Specs](http://search.maven.org/remotecontent?filepath=com/github/markusbernhardt/robotframework-selenium2library-java/1.4.0.6/robotframework-selenium2library-java-1.4.0.6-libdoc.xml) + and place it in your PYTHONPATH. + This Java port of the existing Selenium2Library was created to enable the usage of a Selenium 2 library with Jython. * Python Selenium2Library needs Python 2.6 upwards @@ -25,41 +37,21 @@ your pom.xml: com.github.markusbernhardt robotframework-selenium2library-java - 1.2.0.12 + 1.4.0.6 test -If you cannot use the robotframework-maven-plugin you can use the [jar-with-dependencies](http://search.maven.org/remotecontent?filepath=com/github/markusbernhardt/robotframework-selenium2library-java/1.2.0.12/robotframework-selenium2library-java-1.2.0.12-jar-with-dependencies.jar), which contains all required libraries. +If you cannot use the robotframework-maven-plugin you can use the +[jar-with-dependencies](http://search.maven.org/remotecontent?filepath=com/github/markusbernhardt/robotframework-selenium2library-java/1.4.0.6/robotframework-selenium2library-java-1.4.0.6-jar-with-dependencies.jar), +which contains all required libraries. -If you want more control and feel adventurous you could you use this [jar](http://search.maven.org/remotecontent?filepath=com/github/markusbernhardt/robotframework-selenium2library-java/1.2.0.12/robotframework-selenium2library-java-1.2.0.12.jar) and provide all required libraries from this [list](DEPENDENCIES.md) on your own. +If you want more control and feel adventurous you could you use this +[jar](http://search.maven.org/remotecontent?filepath=com/github/markusbernhardt/robotframework-selenium2library-java/1.4.0.6/robotframework-selenium2library-java-1.4.0.6.jar) +and provide all required libraries from this [list](DEPENDENCIES.md) on your own. Differences ----------- -* Robotframework does not support providing arguments to Java libraries - - With Python it is possible to provide arguments to libraries - - *** Settings *** - Library Selenium2Library 15 30 run_on_failure=Log Source - - This is not possible with a Java Library. But it can be done in a way - that works with Java and Python - - *** Settings *** - Library Selenium2Library - - *** Keywords *** - Open - Open Browser - Set Selenium Timeout 15 - Set Selenium Implicit Wait 30 - Register Keyword To Run On Failure Log Source - -* Robotframework does not support named arguments with Java libraries - - It is not possible to use named arguments. See User Guide 2.2.2 - * Selenium Speed not implemented Setting the Selenium Speed is deprecated several years and not @@ -73,134 +65,6 @@ Differences * There is a reason, why this is not implemented in WebDriver. It's a bad idea. -Enhancements ------------- - -
-
More browser supported
-
-
    -
  • ipad
  • -
  • safari
  • -
-
- -
Custom JavaScript Locators
-
We added the keyword Add Location Strategy. This keyword is somewhat -analogous to the SeleniumLibrary (RC) keyword. It can be used to register -a JavaScript function as locator with the specified strategy name. The -registered function has to return a WebElement, a List of WebElements or -null. This feature was requested in issue #1. - -Since 1.2.0.1 an optional delimiter can be specified. See #3 for more info. - - - -
KeywordArguments
Add Location Strategystrategy_name, function_definition, delimiter=NONE
-
-
- -A small example: - - Selenium2Library.Add Location Strategy elementById return window.document.getElementById(arguments[0]); - Input Text elementById=firstName Max - -A small example with delimiter: - - Selenium2Library.Add Location Strategy elementByClassname return window.document.getElementsByClassName(arguments[0])[arguments[1]]; , - Input Text elementByClassname=inputField,0 Max - Input Text elementByClassname=inputField,1 Mustermann - - -
-
Proxy Handling
-
We added proper proxy handling for RemoteWebDriver instances. This is -needed, if you want to connect to an external Selenium grid through a -local HTTP proxy. Our implementation handles BASIC, DIGEST and NTLM -based authentication schemes correctly. We added the following keyword. - - - -
KeywordArguments
Set Remote Web Driver Proxyhost,port,user=NONE,password=NONE,domain=NONE,workstation=NONE
-Some additional info: -
    -
  • If you set a proxy, it will be used for all subsequent calls of Open Browser
  • -
  • You can remove the proxy by calling: Set Remote Web Driver Proxy ${EMPTY} ${EMPTY}
  • -
  • If you provide no username, we are looking for a username at the Java property http.proxyUser and the environment variables HTTP_PROXY and http_proxy. If we find a username, it is only used, if the host and port also match.
  • -
  • If you provide no password, we are looking for a password at the Java property http.proxyUser and the environment variables HTTP_PROXY and http_proxy. If we find a password, it is only used, if the host, port and username also match.
  • -
  • If you provide a domain, we use NTLM based authentication -
  • If you provide no workstation and we use NTLM based authentication, we use the hostname as workstation name -
-
- -
Waiting
-
Our application is heavily using AJAX. So much more waiting keywords -are needed. The list of new keywords: - - - - - - - - - - - - - - - - - - - - -
KeywordArguments
Element Should Be Clickablelocator,message=NONE
Element Should Not Be Clickablelocator,message=NONE
Element Should Be Selectedlocator,message=NONE
Element Should Not Be Selectedlocator,message=NONE
Element Should Not Containlocator,expected,message=NONE
Element Should Text Should Not Belocator,expected,message=NONE
Wait Until Element Is Clickablelocator,timeout=NONE,message=NONE
Wait Until Element Is Successfully Clickedlocator,timeout=NONE,message=NONE
Wait Until Element Is Not Clickablelocator,timeout=NONE,message=NONE
Wait Until Element Is Selectedlocator,timeout=NONE,message=NONE
Wait Until Element Is Not Selectedlocator,timeout=NONE,message=NONE
Wait Until Element Is Not Visiblelocator,timeout=NONE,message=NONE
Wait Until Page Not Containscondition,timeout=NONE,message=NONE
Wait Until Page Not Contains Elementlocator,timeout=NONE,message=NONE
Wait Until Title Istitle,timeout=NONE,message=NONE
Wait Until Title Is Nottitle,timeout=NONE,message=NONE
Wait Until Title Containstitle,timeout=NONE,message=NONE
Wait Until Title Not Containstitle,timeout=NONE,message=NONE
-
- -
XPath Count
-
We extended the following keywords to be called with a -xpath statement that optionally can start with "xpath=". - - - - -
KeywordArguments
Get Matching Xpath Countxpath
Xpath Should Match X Timesxpath, expected_xpath_count, message=NONE, loglevel=INFO
-
- -
Remote Session Id
-
We introduced two keywords to get the session id from the -RemoteWebDriver. This is mainly needed to identify the test -session at external grids like SauceLabs. - - - - -
KeywordArguments
Get Remote Session Id
Log Remote Session Id
-
- -
Remote Capabilities
-
According to the Javadoc no error is thrown, if a session cannot support a capability that is requested -in the desired capabilities. Therefore it can happen that you are silently running your tests on the wrong -environment. With the following two keywords you can control what capabilities the actual session supports. - - - - -
KeywordArguments
Get Remote Capabilities
Log Remote Capabilities
-
- -
System Info
-
The following two keywords are outputting the current system info as in the WebDriverException. - - - - -
KeywordArguments
Get System Info
Log System Info
-
-
- Demo ---- @@ -208,3 +72,12 @@ This is a maven project. If you have firefox installed, you can execute the unit tests with: mvn integration-test + +Getting Help +------------ + +The [user group for Robot Framework](https://groups.google.com/forum/#!forum/robotframework-users) +is the best place to get help. Consider including in the post: +* Full description of what you are trying to do and expected outcome +* Version number of Selenium2Library, Robot Framework, and Selenium +* StackTraces or other debug output containing error information \ No newline at end of file diff --git a/pom.xml b/pom.xml index 18195b6..75d4e90 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.github.markusbernhardt robotframework-selenium2library-java - 1.2.0.13-SNAPSHOT + 1.4.0.8-SNAPSHOT jar Robot Framework :: Selenium2Library @@ -20,8 +20,11 @@ UTF-8 true true + 1.7.3 1.6 - 2.8 + 1.0.4 + 2.8.3 + Selenium2Library @@ -46,20 +49,30 @@ + + org.robotframework + javalib-core + 1.2 + + + com.github.markusbernhardt + xml-doclet + ${xml.doclet.version} + org.seleniumhq.selenium selenium-server - 2.33.0 + 2.43.1 com.opera operadriver - 1.1 + 1.5 com.github.detro.ghostdriver phantomjsdriver - 1.0.3 + 1.1.0 org.robotframework @@ -69,7 +82,27 @@ org.aspectj aspectjrt - 1.6.11 + ${aspectj.version} + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.seleniumhq.selenium + selenium-remote-driver + 2.43.1 + + + io.selendroid + selendroid-client + 0.11.0 + + + io.appium + java-client + 2.0.0 @@ -99,10 +132,6 @@ org.codehaus.mojo aspectj-maven-plugin 1.4 - - ${java.version} - true - @@ -110,22 +139,66 @@ + + ${java.version} + ${java.version} + ${java.version} + true + + + + org.aspectj + aspectjtools + ${aspectj.version} + + + org.robotframework robotframework-maven-plugin - 1.0.8 + 1.2 + test + integration-test run + + debug + ${project.basedir}/src/test/robotframework/testsuites + + + + html + package + + libdoc + + + + ${project.build.directory} + ${keywords.class}.html + ${keywords.class} + + + + + xml + package + + libdoc + + + + ${project.build.directory} + ${keywords.class}.xml + ${keywords.class} + + - - debug - ${project.basedir}/src/test/robotframework/testsuites - org.robotframework @@ -135,7 +208,7 @@ - + org.apache.maven.plugins maven-compiler-plugin 2.5.1 @@ -149,6 +222,7 @@ + org.apache.maven.plugins maven-assembly-plugin 2.4 @@ -167,6 +241,38 @@ + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + xml-doclet + prepare-package + + javadoc + + + com.github.markusbernhardt.xmldoclet.XmlDoclet + -d ${project.build.directory}/classes -filename + com/github/markusbernhardt/selenium2library/Selenium2Library.javadoc + false + ${java.home}/../bin + + *.java + com/github/markusbernhardt/selenium2library/Selenium2Library.java + com/github/markusbernhardt/selenium2library/keywords/*.java + + + com.github.markusbernhardt + xml-doclet + ${xml.doclet.version} + + + + + @@ -209,18 +315,65 @@ - - org.apache.maven.plugins - maven-javadoc-plugin - - false - - http://java.sun.com/javase/6/docs/api/ - - - + + + sonatype-oss-release + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + false + + http://java.sun.com/javase/7/docs/api/ + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.8 + + + attach-artifacts + package + + attach-artifact + + + + + ${project.build.directory}/${keywords.class}.html + html + libdoc + + + ${project.build.directory}/${keywords.class}.xml + xml + libdoc + + + + + + + + + + + diff --git a/src/main/aspect/com/github/markusbernhardt/selenium2library/aspects/RunOnFailureAspect.aj b/src/main/aspect/com/github/markusbernhardt/selenium2library/aspects/RunOnFailureAspect.aj index 3c66443..d4d7d9e 100644 --- a/src/main/aspect/com/github/markusbernhardt/selenium2library/aspects/RunOnFailureAspect.aj +++ b/src/main/aspect/com/github/markusbernhardt/selenium2library/aspects/RunOnFailureAspect.aj @@ -3,7 +3,7 @@ package com.github.markusbernhardt.selenium2library.aspects; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterThrowing; -import com.github.markusbernhardt.selenium2library.Selenium2Library; +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywords; public aspect RunOnFailureAspect { @@ -18,7 +18,7 @@ public aspect RunOnFailureAspect { return; } - ((Selenium2Library) thisJoinPoint.getTarget()).runOnFailureByAspectJ(); + ((RunOnFailureKeywords) thisJoinPoint.getTarget()).runOnFailureByAspectJ(); lastThrowable.set(t); } } diff --git a/src/main/java/Selenium2Library.java b/src/main/java/Selenium2Library.java index 623362e..f20f94c 100644 --- a/src/main/java/Selenium2Library.java +++ b/src/main/java/Selenium2Library.java @@ -1,3 +1,19 @@ -public class Selenium2Library extends - com.github.markusbernhardt.selenium2library.Selenium2Library { +public class Selenium2Library extends com.github.markusbernhardt.selenium2library.Selenium2Library { + + public Selenium2Library(String timeout, String implicitWait, String runOnFailure) { + super(timeout, implicitWait, runOnFailure); + } + + public Selenium2Library(String timeout, String implicitWait) { + super(timeout, implicitWait); + } + + public Selenium2Library(String timeout) { + super(timeout); + } + + public Selenium2Library() { + super(); + } + } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/RunOnFailureKeywords.java b/src/main/java/com/github/markusbernhardt/selenium2library/RunOnFailureKeywords.java new file mode 100644 index 0000000..aa14a0c --- /dev/null +++ b/src/main/java/com/github/markusbernhardt/selenium2library/RunOnFailureKeywords.java @@ -0,0 +1,13 @@ +package com.github.markusbernhardt.selenium2library; + +public interface RunOnFailureKeywords { + + /** + * This method is called by the + * com.github.markusbernhardt.selenium2library.aspects.RunOnFailureAspect in + * case a exception is thrown in one of the public methods of a keyword + * class. + */ + void runOnFailureByAspectJ(); + +} diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/RunOnFailureKeywordsAdapter.java b/src/main/java/com/github/markusbernhardt/selenium2library/RunOnFailureKeywordsAdapter.java new file mode 100644 index 0000000..1898da4 --- /dev/null +++ b/src/main/java/com/github/markusbernhardt/selenium2library/RunOnFailureKeywordsAdapter.java @@ -0,0 +1,23 @@ +package com.github.markusbernhardt.selenium2library; + +import org.robotframework.javalib.annotation.Autowired; + +import com.github.markusbernhardt.selenium2library.keywords.RunOnFailure; + +public abstract class RunOnFailureKeywordsAdapter implements RunOnFailureKeywords { + + @Autowired + private RunOnFailure runOnFailure; + + /** + * This method is called by the + * com.github.markusbernhardt.selenium2library.aspects.RunOnFailureAspect in + * case a exception is thrown in one of the public methods of a keyword + * class. + */ + @Override + public void runOnFailureByAspectJ() { + runOnFailure.runOnFailure(); + } + +} diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/Selenium2Library.java b/src/main/java/com/github/markusbernhardt/selenium2library/Selenium2Library.java index 25c55fe..4d61611 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/Selenium2Library.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/Selenium2Library.java @@ -1,98 +1,538 @@ package com.github.markusbernhardt.selenium2library; - import java.io.File; import java.util.ResourceBundle; -import com.github.markusbernhardt.selenium2library.keywords.Selenium2LibraryEnhancement; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.library.AnnotationLibrary; + +import com.github.markusbernhardt.selenium2library.keywords.BrowserManagement; +import com.github.markusbernhardt.selenium2library.keywords.Cookie; +import com.github.markusbernhardt.selenium2library.keywords.Element; +import com.github.markusbernhardt.selenium2library.keywords.FormElement; +import com.github.markusbernhardt.selenium2library.keywords.JavaScript; +import com.github.markusbernhardt.selenium2library.keywords.Logging; +import com.github.markusbernhardt.selenium2library.keywords.RunOnFailure; +import com.github.markusbernhardt.selenium2library.keywords.Screenshot; +import com.github.markusbernhardt.selenium2library.keywords.SelectElement; +import com.github.markusbernhardt.selenium2library.keywords.TableElement; +import com.github.markusbernhardt.selenium2library.keywords.Waiting; +import com.github.markusbernhardt.selenium2library.utils.Javadoc2Libdoc; /** - * Robotframework Library. All public methods are keywords. + * Selenium2Library is a web testing library for the Robot Framework and was + * originally written in Python. This is the Java port of the Selenium 2 + * (WebDriver) Python library for Robot Framework. It uses the Selenium 2 + * (WebDriver) libraries internally to control a web browser. See WebDriver for more + * information on Selenium 2 and WebDriver. It runs tests in a real browser + * instance and should work with most modern browsers and can be used with the + * Jython interpreter or any other Java application.
+ *
+ * Before running tests
+ * Prior to running test cases using Selenium2Library, the library must be + * imported into your Robot Framework test suite (see importing section), and + * the `Open Browser` keyword must be used to open a browser to the desired + * location.
+ *
+ * Locating elements
+ * All keywords in Selenium2Library that need to find an element on the page + * take an locator argument.
+ *
+ * Key attributes
+ * By default, when a locator value is provided, it is matched against the key + * attributes of the particular element type. The attributes id and + * name are key attributes to all elements.
+ *
+ * List of key attributes: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Element TypeKey Attributes
A@id,@name,@href,text
IMG@id,@name,@src,@alt
INPUT@id,@name,@value,@src
BUTTON@id,@name,@value,text
*@id,@name
+ *
+ * Example: + * + * + * + * + * + *
Click Elementmy_element
+ *
+ * Locator strategies
+ * It is also possible to specify the approach Selenium2Library should take to + * find an element by specifying a locator strategy with a locator prefix.
+ *
+ * Supported strategies are: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
StrategyExampleDescription
identifierClick Element | identifier=my_elementMatches by @id or @name attribute
idClick Element | id=my_elementMatches by @id attribute
nameClick Element | name=my_elementMatches by @name attribute
xpathClick Element | xpath=//div[@id='my_element']Matches by arbitrary XPath expression
domClick Element | dom=document.images[56]Matches by arbitrary DOM expression
linkClick Element | link=My LinkMatches by the link text
cssClick Element | css=div.my_classMatches by CSS selector
jqueryClick Element | jquery=div.my_classMatches by jQuery/sizzle selector
sizzleClick Element | sizzle=div.my_classMatches by jQuery/sizzle selector
tagClick Element | tag=divMatches by HTML tag name
+ *
+ * Locating tables
+ * Table related keywords, such as `Table Should Contain`, work differently. By + * default, when a table locator value is provided, it will search for a table + * with the specified id attribute.
+ *
+ * Example:
+ * + * + * + * + * + * + *
Table Should Containmy_tabletext
+ *
+ * More complex table locator strategies: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
StrategyExampleDescription
xpathTable Should Contain | xpath=//table/[@name="my_table"] | textMatches by arbitrary XPath expression
cssTable Should Contain | css=table.my_class | textMatches by CSS selector
+ *
+ * Custom location strategies
+ * It is also possible to register custom location strategies. See `Add Location + * Strategy` for details about custom location strategies.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + *
Add Location Strategycustomreturn window.document.getElementById(arguments[0]);
Input Textcustom=firstNameMax
+ *
+ * Timeouts
+ * There are several Wait ... keywords that take timeout as an argument. + * All of these timeout arguments are optional. The timeout used by all of them + * can be set globally using the `Set Selenium Timeout keyword`.
+ *
+ * All timeouts can be given as numbers considered seconds (e.g. 0.5 or 42) or + * in Robot Framework's time syntax (e.g. '1.5 seconds' or '1 min 30 s'). See Time Format for details about the time syntax.
+ *
+ * Log Level
+ * There are several keywords that take timeout as an argument. All of + * these timeout arguments are optional. The default is usually INFO.
+ *
+ * List of log levels: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Log LevelDescription
DEBUG
INFO
HTMLSame as INFO, but message is in HTML format
TRACE
WARN
*/ -public class Selenium2Library extends Selenium2LibraryEnhancement { +public class Selenium2Library extends AnnotationLibrary { + + /** + * The list of keyword patterns for the AnnotationLibrary + */ + public static final String KEYWORD_PATTERN = "com/github/markusbernhardt/selenium2library/keywords/**/*.class"; /** - * This means the same instance of this class is used throughout the - * lifecycle of a Robot Framework test execution. + * The javadoc to libdoc converter */ - public static String ROBOT_LIBRARY_SCOPE = "GLOBAL"; + public static final Javadoc2Libdoc JAVADOC_2_LIBDOC = new Javadoc2Libdoc(Selenium2Library.class); /** - * Documentation is in text format. + * The library documentation is written in HTML */ - public static String ROBOT_LIBRARY_DOC_FORMAT = "TEXT"; + public static final String ROBOT_LIBRARY_DOC_FORMAT = "HTML"; + + /** + * The scope of this library is global. + */ + public static final String ROBOT_LIBRARY_SCOPE = "GLOBAL"; /** * The actual version of this library. Loaded from maven project. */ - public static String ROBOT_LIBRARY_VERSION; + public static final String ROBOT_LIBRARY_VERSION = loadRobotLibraryVersion(); - static { - /** - * Load the version from file - */ + private static String loadRobotLibraryVersion() { try { - ROBOT_LIBRARY_VERSION = ResourceBundle.getBundle( - Selenium2Library.class.getCanonicalName().replace(".", - File.separator)).getString("version"); + return ResourceBundle.getBundle(Selenium2Library.class.getCanonicalName().replace(".", File.separator)) + .getString("version"); } catch (RuntimeException e) { - ROBOT_LIBRARY_VERSION = "unknown"; + return "unknown"; } } - /** - * Default constructor - */ public Selenium2Library() { - this(5.0); + this("5.0"); } - /** - * Constructor - * - * @param timeout - * Default timeout in seconds for all wait methods - */ - public Selenium2Library(double timeout) { - this(timeout, 0.0); + public Selenium2Library(String timeout) { + this(timeout, "0.0"); } - /** - * Constructor - * - * @param timeout - * Default timeout in seconds for all wait methods - * @param implicitWait - * Selenium implicit wait time in seconds - */ - public Selenium2Library(double timeout, double implicitWait) { + public Selenium2Library(String timeout, String implicitWait) { this(timeout, implicitWait, "Capture Page Screenshot"); } /** - * Constructor + * Selenium2Library can be imported with optional arguments.
+ *
+ * timeout is the default timeout used to wait for all waiting + * actions. It can be changed later with `Set Selenium Timeout`.
+ *
+ * implicitWait is the implicit timeout that Selenium waits, when + * looking for elements. It can be changed later with `Set Selenium Implicit + * Wait`. See WebDriver: Advanced Usage of the SeleniumHQ documentation for + * details about WebDriver's implicit wait functionality.
+ *
+ * runOnFailure specifies the name of a keyword (from any available + * libraries) to execute when a Selenium2Library keyword fails. By default + * `Capture Page Screenshot` will be used to take a screenshot of the + * current page. Using the value \"Nothing\" will disable this feature + * altogether. See `Register Keyword To Run On Failure` keyword for details + * about this functionality.
+ *
+ * Examples: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
LibrarySelenium2Library
LibrarySelenium2Library15# Sets timeout to 15 seconds
LibrarySelenium2Library05# Sets timeout to 0 seconds and implicitWait to 5 seconds
LibrarySelenium2Library05Log Source# Sets timeout to 0 seconds, implicitWait to 5 seconds and runs `Log + * Source` on failure
LibrarySelenium2Library05Nothing# Sets timeout to 0 seconds, implicitWait to 5 seconds and does + * nothing on failure
* * @param timeout - * Default timeout in seconds for all wait methods + * Default=5.0. Optional custom timeout. * @param implicitWait - * Selenium implicit wait time in seconds - * @param runOnFailureKeyword - * Keyword to run opn failure + * Default=0.0. Optional custom implicit wait time. + * @param runOnFailure + * Default=Capture Page Screenshot. Optional custom keyword to + * run on failure. */ - public Selenium2Library(double timeout, double implicitWait, - String runOnFailureKeyword) { - this.timeout = timeout; - this.implicitWait = implicitWait; - this.runOnFailureKeyword = runOnFailureKeyword; + public Selenium2Library(String timeout, String implicitWait, String runOnFailure) { + super(); + addKeywordPattern(KEYWORD_PATTERN); + createKeywordFactory(); // => init annotations + browserManagement.setSeleniumTimeout(timeout); + browserManagement.setSeleniumImplicitWait(implicitWait); + this.runOnFailure.registerKeywordToRunOnFailure(runOnFailure); } + // ############################## + // Autowired References + // ############################## + + /** + * Instantiated BrowserManagement keyword bean + */ + @Autowired + protected BrowserManagement browserManagement; + + /** + * Instantiated Cookie keyword bean + */ + @Autowired + protected Cookie cookie; + + /** + * Instantiated Element keyword bean + */ + @Autowired + protected Element element; + + /** + * Instantiated FormElement keyword bean + */ + @Autowired + protected FormElement formElement; + + /** + * Instantiated JavaScript keyword bean + */ + @Autowired + protected JavaScript javaScript; + + /** + * Instantiated Logging keyword bean + */ + @Autowired + protected Logging logging; + + /** + * Instantiated RunOnFailure keyword bean + */ + @Autowired + protected RunOnFailure runOnFailure; + + /** + * Instantiated Screenshot keyword bean + */ + @Autowired + protected Screenshot screenshot; + + /** + * Instantiated SelectElement keyword bean + */ + @Autowired + protected SelectElement selectElement; + /** - * This method is called by the - * com.github.markusbernhardt.selenium2library.aspects.RunOnFailureAspect in - * case a exception is thrown in one of the public methods of a keyword - * class. + * Instantiated TableElement keyword bean */ - public void runOnFailureByAspectJ() { - runOnFailure(); + @Autowired + protected TableElement tableElement; + + /** + * Instantiated Waiting keyword bean + */ + @Autowired + protected Waiting waiting; + + // ############################## + // Getter / Setter + // ############################## + + public BrowserManagement getBrowserManagement() { + return browserManagement; + } + + public Cookie getCookie() { + return cookie; + } + + public Element getElement() { + return element; + } + + public FormElement getFormElement() { + return formElement; + } + + public JavaScript getJavaScript() { + return javaScript; + } + + public Logging getLogging() { + return logging; + } + + public RunOnFailure getRunOnFailure() { + return runOnFailure; } + public Screenshot getScreenshot() { + return screenshot; + } + + public SelectElement getSelectElement() { + return selectElement; + } + + public TableElement getTableElement() { + return tableElement; + } + + public Waiting getWaiting() { + return waiting; + } + + // ############################## + // Public Methods + // ############################## + + @Override + public Object runKeyword(String keywordName, Object[] args) { + return super.runKeyword(keywordName, toStrings(args)); + } + + @Override + public String getKeywordDocumentation(String keywordName) { + String keywordDocumentation = JAVADOC_2_LIBDOC.getKeywordDocumentation(keywordName); + if (keywordDocumentation == null) { + try { + return super.getKeywordDocumentation(keywordName); + } catch (NullPointerException e) { + return ""; + } + } + return keywordDocumentation; + } + + public static Selenium2Library getLibraryInstance() throws ScriptException { + ScriptEngine engine = new ScriptEngineManager().getEngineByName("python"); + engine.put("library", "Selenium2Library"); + engine.eval("from robot.libraries.BuiltIn import BuiltIn"); + engine.eval("instance = BuiltIn().get_library_instance(library)"); + return (Selenium2Library) engine.get("instance"); + } + + // ############################## + // Internal Methods + // ############################## + + /** + * Convert all arguments in the object array to string + */ + protected Object[] toStrings(Object[] args) { + Object[] newArgs = new Object[args.length]; + for (int i = 0; i < newArgs.length; i++) { + if (args[i] == null || args[i].getClass().isArray()) { + newArgs[i] = args[i]; + } else { + newArgs[i] = args[i].toString(); + } + } + return newArgs; + } } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/BrowserManagement.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/BrowserManagement.java index 88a9270..4918b33 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/BrowserManagement.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/BrowserManagement.java @@ -1,12 +1,16 @@ package com.github.markusbernhardt.selenium2library.keywords; import java.io.File; +import java.io.IOException; import java.lang.reflect.Field; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Map.Entry; import java.util.concurrent.TimeUnit; import org.apache.http.HttpHost; @@ -16,31 +20,44 @@ import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.DefaultHttpRoutePlanner; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.openqa.selenium.Dimension; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; -import org.openqa.selenium.android.AndroidDriver; +import io.selendroid.SelendroidDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxProfile; import org.openqa.selenium.htmlunit.HtmlUnitDriver; import org.openqa.selenium.ie.InternetExplorerDriver; -import org.openqa.selenium.iphone.IPhoneDriver; +import io.appium.java_client.ios.IOSDriver; import org.openqa.selenium.phantomjs.PhantomJSDriver; import org.openqa.selenium.remote.Augmenter; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.HttpCommandExecutor; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.safari.SafariDriver; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywordOverload; +import org.robotframework.javalib.annotation.RobotKeywords; +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; import com.github.markusbernhardt.selenium2library.Selenium2LibraryFatalException; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; +import com.github.markusbernhardt.selenium2library.locators.ElementFinder; import com.github.markusbernhardt.selenium2library.locators.WindowManager; import com.github.markusbernhardt.selenium2library.utils.Robotframework; import com.github.markusbernhardt.selenium2library.utils.WebDriverCache; import com.github.markusbernhardt.selenium2library.utils.WebDriverCache.SessionIdAliasWebDriverTuple; import com.opera.core.systems.OperaDriver; -public abstract class BrowserManagement { +@SuppressWarnings("deprecation") +@RobotKeywords +public class BrowserManagement extends RunOnFailureKeywordsAdapter { public String remoteWebDriverProxyHost = ""; public String remoteWebDriverProxyPort = ""; @@ -64,282 +81,1172 @@ public abstract class BrowserManagement { */ protected double implicitWait = 0; + /** + * Instantiated Logging keyword bean + */ + @Autowired + protected Logging logging; + + /** + * Instantiated Element keyword bean + */ + @Autowired + protected Element element; + + // ############################## + // Getter / Setter + // ############################## + + public WebDriverCache getWebDriverCache() { + return webDriverCache; + } + + public WebDriver getCurrentWebDriver() { + return webDriverCache.getCurrent(); + } + + public double getTimeout() { + return timeout; + } + // ############################## // Keywords // ############################## + @RobotKeywordOverload + public void addLocationStrategy(String strategyName, String functionDefinition) { + addLocationStrategy(strategyName, functionDefinition, null); + } + + /** + * Registers a JavaScript function as locator with the specified strategy + * name.
+ *
+ * The registered function has to return a WebElement, a List of WebElements + * or null. Optionally a delimiter can be given to split the value of the + * locator in multiple arguments when executing the JavaScript function.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + *
Add Location StrategybyIdreturn window.document.getElementById(arguments[0]);
Input TextbyId=firstNameMax
+ *
+ * Example with delimiter: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Add Location StrategybyClassnamereturn + * window.document.getElementsByClassName(arguments[0])[arguments[1]];,
Input TextbyClassname=input,3Max
+ * + * @param strategyName + * Name of the location strategy. + * @param functionDefinition + * The JavaScript function to register. + * @param delimiter + * Default=NONE. The delimiter to split the given locator value + */ + @RobotKeyword + @ArgumentNames({ "strategyName", "functionDefinition", "delimiter=NONE" }) + public void addLocationStrategy(String strategyName, String functionDefinition, String delimiter) { + ElementFinder.addLocationStrategy(strategyName, functionDefinition, delimiter); + } + + /** + * Closes the current browser instance.
+ * + * @see BrowserManagement#closeAllBrowsers + * @see BrowserManagement#openBrowser + * @see BrowserManagement#switchBrowser + */ + @RobotKeyword public void closeBrowser() { if (webDriverCache.getCurrentSessionId() != null) { - debug(String.format("Closing browser with session id %s", - webDriverCache.getCurrentSessionId())); + logging.debug(String.format("Closing browser with session id %s", webDriverCache.getCurrentSessionId())); webDriverCache.close(); } } + @RobotKeywordOverload public String openBrowser(String url) throws Throwable { return openBrowser(url, "firefox"); } + @RobotKeywordOverload public String openBrowser(String url, String browserName) throws Throwable { return openBrowser(url, browserName, null); } - public String openBrowser(String url, String browserName, String alias) - throws Throwable { + @RobotKeywordOverload + public String openBrowser(String url, String browserName, String alias) throws Throwable { return openBrowser(url, browserName, alias, null); } - public String openBrowser(String url, String browserName, String alias, - String remoteUrl) throws Throwable { + @RobotKeywordOverload + public String openBrowser(String url, String browserName, String alias, String remoteUrl) throws Throwable { return openBrowser(url, browserName, alias, remoteUrl, null); } - public String openBrowser(String url, String browserName, String alias, - String remoteUrl, String desiredCapabilities) throws Throwable { - return openBrowser(url, browserName, alias, remoteUrl, - desiredCapabilities, null); + public String openBrowser(String url, String browserName, String alias, String remoteUrl, String desiredCapabilities) + throws Throwable { + return openBrowser(url, browserName, alias, remoteUrl, desiredCapabilities, null); } - public String openBrowser(String url, String browserName, String alias, - String remoteUrl, String desiredCapabilities, String ffProfileDir) - throws Throwable { + /** + * Opens a new browser instance to given URL.
+ *
+ * Possible values for browser are as follows: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
firefoxFireFox
ffFireFox
internetexplorerInternet Explorer
ieInternet Explorer
googlechromeGoogle Chrome
gcGoogle Chrome
chromeGoogle Chrome
operaOpera
phantomjsPhantomJS
htmlunitwithjsHTMLUnit with Javascipt support
safariSafari
ipadiPad
iphoneiPhone
androidAndroid
htmlunitHTMLUnit
+ *
+ * Returns the index of the newly created browser instance which can be used + * later to switch back to it. Index starts from 1 and is reset back to it + * when the `Close All Browsers` keyword is used.
+ *
+ * DesiredCapabilities
+ * The DesiredCapabilities can be specified in a simple key:value format or + * as a JSON object. With the JSON format more complex parameters, like the + * proxy, can be configured.
+ *
+ * Example of desiredCapabilities as simple string:
+ * + * + * + * + *
platform:Windows 8,browserName:firefox,version:25
+ *
+ * Example of desiredCapabilities as JSON object:
+ * + * + * + * + *
+ * {
+ *  "platform":"Windows 8",
+ *  "browserName":"firefox",
+ *  "version":"25",
+ *  "proxy":
+ *  {
+ *   "proxyType":"manual",
+ *   "httpProxy":"localhost:8118"
+ *  }
+ * }
+ *
+ *
+ * BrowserOptions
+ * The BrowserOptions can be specified as JSON object to set more complex + * browser specific parameters. At the moment only the following browsers + * with the listed options are implemented.
+ *
+ * Firefox: + * + * + * + * + *
+ * {
+ *  "preferences":
+ *  {
+ *   "extensions.firebug.currentVersion":"1.8.1",
+ *   "extensions.firebug.addonBarOpened":true,
+ *   "extensions.firebug.enableSites":true
+ *  }
+ *  "extensions":
+ *  [
+ *   "firebug-1.8.1.xpi",
+ *   "modify_headers-0.7.1.1-fx.xpi"
+ *  ]
+ * }
+ *
+ *
+ * Internet Explorer
+ * Note, that you will encounter strange behavior, if you open multiple + * Internet Explorer browser instances. That is also why `Switch Browser` + * only works with one IE browser at most. For more information see: Strange errors with multiple IE instances
+ * + * @param url + * The URL to open in the newly created browser instance. + * @param browserName + * Default=firefox. Optional name of the browser to start. + * @param alias + * Default=NONE. Optional alias for the newly created browser + * instance. The alias can be used later for switching between + * browsers instances, just as returned index. + * @param remoteUrl + * Default=NONE. Optional remote grid URL. When specified no + * local WebDriver instance is created, but a network connection + * to a Selenium 2 WebDriver Grid Hub at the given URL is opened. + * @param desiredCapabilities + * Default=NONE. Optional desired capabilities of the newly + * created remote browser instances can be specified in a simple + * key1:val1,key2:val2 format or as a JSON object (see examples + * above). Used to communicate to the remote grid, which kind of + * browser, etc. should be used. For more information see: DesiredCapabilities + * @return The index of the newly created browser instance. + * + * @see BrowserManagement#closeAllBrowsers + * @see BrowserManagement#closeBrowser + * @see BrowserManagement#switchBrowser + */ + @RobotKeyword + @ArgumentNames({ "url", "browserName=firefox", "alias=NONE", "remoteUrl=NONE", "desiredCapabilities=NONE", + "browserOptions=NONE" }) + public String openBrowser(String url, String browserName, String alias, String remoteUrl, + String desiredCapabilities, String browserOptions) throws Throwable { try { - info("browserName: " + browserName); + logging.info("browserName: " + browserName); if (remoteUrl != null) { - info(String - .format("Opening browser '%s' to base url '%s' through remote server at '%s'", - browserName, url, remoteUrl)); + logging.info(String.format("Opening browser '%s' to base url '%s' through remote server at '%s'", + browserName, url, remoteUrl)); } else { - info(String.format("Opening browser '%s' to base url '%s'", - browserName, url)); + logging.info(String.format("Opening browser '%s' to base url '%s'", browserName, url)); } - WebDriver webDriver = createWebDriver(browserName, - desiredCapabilities, ffProfileDir, remoteUrl); + WebDriver webDriver = createWebDriver(browserName, desiredCapabilities, remoteUrl, browserOptions); webDriver.get(url); String sessionId = webDriverCache.register(webDriver, alias); - debug(String.format("Opened browser with session id %s", sessionId)); + logging.debug(String.format("Opened browser with session id %s", sessionId)); return sessionId; } catch (Throwable t) { if (remoteUrl != null) { - warn(String - .format("Opening browser '%s' to base url '%s' through remote server at '%s' failed", - browserName, url, remoteUrl)); + logging.warn(String.format( + "Opening browser '%s' to base url '%s' through remote server at '%s' failed", browserName, url, + remoteUrl)); } else { - warn(String.format( - "Opening browser '%s' to base url '%s' failed", - browserName, url)); + logging.warn(String.format("Opening browser '%s' to base url '%s' failed", browserName, url)); } throw new Selenium2LibraryFatalException(t); } } + /** + * "Switches between active browser instances using an index or an + * alias.
+ *
+ * The index is returned from `Open Browser` and an alias can be given to + * it.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Open Browserhttp://google.comff
Location Should Behttp://google.com
Open Browserhttp://yahoo.comie2nd conn
Location Should Behttp://yahoo.com
Switch Browser1# index
Page Should ContainI'm feeling lucky
Switch Browser2nd conn# alias
Page Should ContainMore Yahoo!
Close All Browsers
+ *
+ * The above example expects that there was no other open browsers when + * opening the first one because it used index '1' when switching to it + * later. If you aren't sure about that you can store the index into a + * variable as below. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
${id} =Open Browserhttp://google.com
# Do something ...
Switch Browser${id}
+ * + * @param indexOrAlias + * the index or alias of the browser instance to switch to + * + * @see BrowserManagement#closeAllBrowsers + * @see BrowserManagement#closeBrowser + * @see BrowserManagement#openBrowser + */ + @RobotKeyword + @ArgumentNames({ "indexOrAlias" }) public void switchBrowser(String indexOrAlias) { try { webDriverCache.switchBrowser(indexOrAlias); - debug(String.format( - "Switched to browser with Selenium session id %s", + logging.debug(String.format("Switched to browser with Selenium session id %s", webDriverCache.getCurrentSessionId())); } catch (Throwable t) { - throw new Selenium2LibraryFatalException(String.format( - "No browser with index or alias '%s' found.", indexOrAlias)); + throw new Selenium2LibraryFatalException(String.format("No browser with index or alias '%s' found.", + indexOrAlias)); } } + /** + * Closes all open browser instances and resets the browser cache.
+ *
+ * After this keyword new indexes returned from `Open Browser` keyword are + * reset to 1. This keyword should be used in test or suite teardown to make + * sure all browsers are closed.
+ * + * @see BrowserManagement#closeBrowser + * @see BrowserManagement#openBrowser + * @see BrowserManagement#switchBrowser + */ + @RobotKeyword public void closeAllBrowsers() { - debug("Closing all browsers"); + logging.debug("Closing all browsers"); webDriverCache.closeAll(); } + /** + * Closes the currently open pop-up window. + */ + @RobotKeyword public void closeWindow() { webDriverCache.getCurrent().close(); } + /** + * Returns the id attributes of all windows known to the current browser + * instance.
+ * + * @return List of window id attributes + * + * @see Logging#logWindowIdentifiers + */ + @RobotKeyword public List getWindowIdentifiers() { - return logList(WindowManager.getWindowIds(webDriverCache.getCurrent()), - "Window Id"); + return toList(WindowManager.getWindowIds(webDriverCache.getCurrent()), "Window Id"); } + /** + * Returns the names of all windows known to the current browser instance.
+ * + * @return List of window names + * + * @see Logging#logWindowNames + */ + @RobotKeyword public List getWindowNames() { - List windowNames = WindowManager.getWindowNames(webDriverCache - .getCurrent()); + List windowNames = WindowManager.getWindowNames(webDriverCache.getCurrent()); if (windowNames.size() != 0 && windowNames.get(0).equals("undefined")) { windowNames.set(0, "selenium_main_app_window"); } - return logList(windowNames, "Window Name"); + return toList(windowNames, "Window Name"); } + /** + * Returns the titles of all windows known to the current browser instance.
+ * + * @return List of window titles + * + * @see Logging#logWindowTitles + */ + @RobotKeyword public List getWindowTitles() { - return logList( - WindowManager.getWindowTitles(webDriverCache.getCurrent()), - "Window Title"); + return toList(WindowManager.getWindowTitles(webDriverCache.getCurrent()), "Window Title"); } + /** + * Maximizes current browser window.
+ */ + @RobotKeyword public void maximizeBrowserWindow() { webDriverCache.getCurrent().manage().window().maximize(); } + /** + * Returns current window size as width then height.
+ *
+ * Example: + * + * + * + * + * + * + *
${width}${height}=Get Window Size
+ * + * @return The window width and height in px. + */ + public Object[] getWindowSize() { + Dimension size = getCurrentWebDriver().manage().window().getSize(); + return new Object[] { Integer.toString(size.width), Integer.toString(size.height) }; + } + + /** + * Sets the width and height of the current window to the + * specified values.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Set Window Size800600
${width}${height}=Get Window Size
Should Be Equal${width}800
Should Be Equal${height}600
+ * + * @param width + * The window width to set in px. + * @param height + * The window height to set in px. + */ + @RobotKeyword + @ArgumentNames({ "width", "height" }) + public void setWindowSize(String width, String height) { + getCurrentWebDriver().manage().window() + .setSize(new Dimension(Integer.parseInt(width), Integer.parseInt(height))); + } + + /** + * Selects the frame identified by locator as current frame.
+ *
+ * Key attributes for frames are id and name. See + * `Introduction` for details about locators.
+ * + * @param locator + * The locator to locate the frame + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void selectFrame(String locator) { - info(String.format("Selecting frame '%s'.", locator)); - List elements = elementFind(locator, true, true); + logging.info(String.format("Selecting frame '%s'.", locator)); + List elements = element.elementFind(locator, true, true); webDriverCache.getCurrent().switchTo().frame(elements.get(0)); } + @RobotKeywordOverload public void selectWindow() { selectWindow(null); } + /** + * Selects the window identified by locator as the context of + * actions.
+ *
+ * If the window is found, all subsequent commands use that window, until + * this keyword is used again. If the window is not found, this keyword + * fails.
+ *
+ * By default, when a locator value is provided, it is matched against the + * title of the window and the javascript name of the window. If multiple + * windows with same identifier are found, the first one is selected.
+ *
+ * The special locator main (default) can be used to select the main window.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Click Linkpopup_link# opens new window
Select WindowpopupName
Title Should BePopup Title
Select Window# Chooses the main window again
+ *
+ * It is also possible to specify the approach Selenium2Library should take + * to find a window by specifying a locator strategy. See `Introduction` for + * details about locators: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
StrategyExampleDescription
titleSelect Window | title=My DocumentMatches by window title
nameSelect Window | name=${name}Matches by window javascript name
urlSelect Window | url=http://google.comMatches by window's current URL
+ * + * @param locator + * The locator to locate the window + */ + @RobotKeyword + @ArgumentNames({ "locator=NONE" }) public void selectWindow(String locator) { WindowManager.select(webDriverCache.getCurrent(), locator); } + /** + * Selects the top frame as the current frame. + */ + @RobotKeyword public void unselectFrame() { webDriverCache.getCurrent().switchTo().defaultContent(); } + /** + * Returns the current location. + * + * @return The current location. + * + * @see Logging#logLocation + */ + @RobotKeyword public String getLocation() { return webDriverCache.getCurrent().getCurrentUrl(); } + /** + * Returns the entire HTML source of the current page or frame. + * + * @return The HTML source. + * + * @see Logging#logSource + */ + @RobotKeyword public String getSource() { return webDriverCache.getCurrent().getPageSource(); } + /** + * Returns the title of current page. + * + * @return The title. + * + * @see Logging#logTitle + */ + @RobotKeyword public String getTitle() { return webDriverCache.getCurrent().getTitle(); } + /** + * Returns the actually supported capabilities of the remote browser + * instance.
+ *
+ * Not all server implementations will support every WebDriver feature. + * Therefore, the client and server should use JSON objects with the + * properties listed below when describing which features a user requests + * that a session support. If a session cannot support a capability that + * is requested in the desired capabilities, no error is thrown; a + * read-only capabilities object is returned that indicates the capabilities + * the session actually supports. For more information see: DesiredCapabilities
+ * + * @return The capabilities of the remote node. + * + * @see Logging#logRemoteCapabilities + */ + @RobotKeyword + public String getRemoteCapabilities() { + if (getCurrentWebDriver() instanceof RemoteWebDriver) { + return ((RemoteWebDriver) getCurrentWebDriver()).getCapabilities().toString(); + } else { + return "No remote session id"; + } + } + + /** + * Returns the session id of the remote browser instance.
+ * + * @return The remote session id. + * + * @see Logging#logRemoteSessionId + */ + @RobotKeyword + public String getRemoteSessionId() { + if (getCurrentWebDriver() instanceof RemoteWebDriver) { + return ((RemoteWebDriver) getCurrentWebDriver()).getSessionId().toString(); + } else { + return "No remote session id"; + } + } + + /** + * Returns basic system information about the execution environment.
+ * + * @return System information. + * + * @see Logging#logSystemInfo + */ + @RobotKeyword + public String getSystemInfo() { + return String.format(" os.name: '%s'\n os.arch: '%s'\n os.version: '%s'\n java.version: '%s'", + System.getProperty("os.name"), System.getProperty("os.arch"), System.getProperty("os.version"), + System.getProperty("java.version")); + } + + /** + * Verify the current page URL is exactly url.
+ * + * @param url + * The URL to verify. + * + * @see BrowserManagement#locationShouldContain + */ + @RobotKeyword + @ArgumentNames({ "url" }) public void locationShouldBe(String url) { String actual = getLocation(); if (!actual.equals(url)) { - throw new Selenium2LibraryNonFatalException(String.format( - "Location should have been '%s' but was '%s'", url, actual)); + throw new Selenium2LibraryNonFatalException(String.format("Location should have been '%s', but was '%s'", + url, actual)); } - info(String.format("Current location is '%s'.", url)); + logging.info(String.format("Current location is '%s'.", url)); } + /** + * Verify the current page URL contains url.
+ * + * @param url + * The URL to verify. + * + * @see BrowserManagement#locationShouldBe + */ + @RobotKeyword + @ArgumentNames({ "url" }) public void locationShouldContain(String url) { String actual = getLocation(); if (!actual.contains(url)) { throw new Selenium2LibraryNonFatalException(String.format( - "Location should have been '%s' but was '%s'", url, actual)); + "Location should have contained '%s', but was '%s'", url, actual)); } - info(String.format("Current location is '%s'.", url)); + logging.info(String.format("Current location is '%s'.", url)); } - public String logLocation() { - String actual = getLocation(); - info(actual); - return actual; - } - - public String logSource() { - return logSource("INFO"); + /** + * Verify the current page title is exactly title.
+ * + * @param title + * The title to verify. + * + * @see BrowserManagement#titleShouldNotBe + * @see BrowserManagement#titleShouldContain + * @see BrowserManagement#titleShouldNotContain + */ + @RobotKeyword + @ArgumentNames({ "title" }) + public void titleShouldBe(String title) { + String actual = getTitle(); + if (!actual.equals(title)) { + throw new Selenium2LibraryNonFatalException(String.format("Title should have been '%s', but was '%s'", + title, actual)); + } + logging.info(String.format("Page title is '%s'.", title)); } - public String logSource(String logLevel) { - String actual = getSource(); - log(actual, logLevel); - return actual; + /** + * Verify the current page title is not exactly title.
+ * + * @param title + * The title to verify. + * + * @see BrowserManagement#titleShouldBe + * @see BrowserManagement#titleShouldContain + * @see BrowserManagement#titleShouldNotContain + */ + @RobotKeyword + @ArgumentNames({ "title" }) + public void titleShouldNotBe(String title) { + String actual = getTitle(); + if (actual.equals(title)) { + throw new Selenium2LibraryNonFatalException(String.format("Title should not have been '%s', but was '%s'", + title, actual)); + } + logging.info(String.format("Page title is '%s'.", title)); } - public String logTitle() { + /** + * Verify the current page title contains title.
+ * + * @param title + * The title to verify. + * + * @see BrowserManagement#titleShouldBe + * @see BrowserManagement#titleShouldNotBe + * @see BrowserManagement#titleShouldNotContain + */ + @RobotKeyword + @ArgumentNames({ "title" }) + public void titleShouldContain(String title) { String actual = getTitle(); - info(actual); - return actual; + if (!actual.contains(title)) { + throw new Selenium2LibraryNonFatalException(String.format("Title should have contained '%s', but was '%s'", + title, actual)); + } + logging.info(String.format("Page title is '%s'.", title)); } - public void titleShouldBe(String title) { + /** + * Verify the current page title does not contain title.
+ * + * @param title + * The title to verify. + * + * @see BrowserManagement#titleShouldBe + * @see BrowserManagement#titleShouldNotBe + * @see BrowserManagement#titleShouldContain + */ + @RobotKeyword + @ArgumentNames({ "title" }) + public void titleShouldNotContain(String title) { String actual = getTitle(); - if (!actual.equals(title)) { + if (actual.contains(title)) { throw new Selenium2LibraryNonFatalException(String.format( - "Title should have been '%s' but was '%s'", title, actual)); + "Title should not have contained '%s', but was '%s'", title, actual)); } - info(String.format("Page title is '%s'.", title)); + logging.info(String.format("Page title is '%s'.", title)); } + /** + * Simulates the user clicking the "back" button on their browser.
+ */ + @RobotKeyword public void goBack() { webDriverCache.getCurrent().navigate().back(); } + /** + * Navigates the active browser instance to the provided URL.
+ * + * @param url + * The URL to open. + */ + @RobotKeyword + @ArgumentNames({ "url" }) public void goTo(String url) { - info(String.format("Opening url '%s'", url)); + logging.info(String.format("Opening url '%s'", url)); webDriverCache.getCurrent().get(url); } + /** + * Simulates user reloading page.
+ */ + @RobotKeyword public void reloadPage() { webDriverCache.getCurrent().navigate().refresh(); } + /** + * (NOT IMPLEMENTED) Returns the delay in seconds that is waited + * after each Selenium command.
+ * + * @return The delay in seconds. + * + * @see BrowserManagement#setSeleniumSpeed + */ + @RobotKeyword public String getSeleniumSpeed() { return Robotframework.secsToTimestr(0); } - public String getSeleniumTimeout() { - return Robotframework.secsToTimestr(timeout); - } - - public String getSeleniumImplicitWait() { - return Robotframework.secsToTimestr(implicitWait); - } - + /** + * (NOT IMPLEMENTED) Sets and returns the delay in seconds that is + * waited after each Selenium command.
+ * + * @param timestr + * The delay in seconds. + * @return The previous delay in seconds. + * + * @see BrowserManagement#getSeleniumSpeed + */ + @RobotKeyword("(NOT IMPLEMENTED)\n\nSets the delay in seconds that is waited after each " + "Selenium command.\n") + @ArgumentNames({ "timestr" }) public String setSeleniumSpeed(String timestr) { return "0s"; } + /** + * Returns the timeout in seconds that is used by various keywords.
+ * + * @return The timeout in seconds. + * + * @see BrowserManagement#setSeleniumTimeout + */ + @RobotKeyword + public String getSeleniumTimeout() { + return Robotframework.secsToTimestr(timeout); + } + + /** + * Sets and returns the timeout in seconds that is used by various keywords.
+ *
+ * There are several Wait ... keywords that take a timeout as an argument. + * All of these timeout arguments are optional. The timeout used by all of + * them can be set globally using this keyword. See `Introduction` for more + * information about timeouts.
+ *
+ * The previous timeout value is returned by this keyword and can be used to + * set the old value back later. The default timeout is 5 seconds, but it + * can be altered in importing the library.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
${orig timeout} =Set Selenium Timeout15 seconds
# Open page that loads slowly
Set Selenium Timeout${orig timeout}# Reset to old value
+ * + * @param timestr + * The timeout in seconds. + * @return The previous timeout in seconds. + * + * @see BrowserManagement#getSeleniumTimeout + */ + @RobotKeyword + @ArgumentNames({ "timestr" }) public String setSeleniumTimeout(String timestr) { String oldWait = getSeleniumTimeout(); timeout = Robotframework.timestrToSecs(timestr); - for (SessionIdAliasWebDriverTuple sessionIdAliasWebDriverTuple : webDriverCache - .getWebDrivers()) { - sessionIdAliasWebDriverTuple.webDriver - .manage() - .timeouts() - .setScriptTimeout((int) (timeout * 1000.0), - TimeUnit.MILLISECONDS); + for (SessionIdAliasWebDriverTuple sessionIdAliasWebDriverTuple : webDriverCache.getWebDrivers()) { + sessionIdAliasWebDriverTuple.webDriver.manage().timeouts() + .setScriptTimeout((int) (timeout * 1000.0), TimeUnit.MILLISECONDS); } return oldWait; } + /** + * Returns the implicit wait time in seconds that is used by Selenium.
+ * + * @return The implicit wait time in seconds. + * + * @see BrowserManagement#setSeleniumImplicitWait + */ + @RobotKeyword + public String getSeleniumImplicitWait() { + return Robotframework.secsToTimestr(implicitWait); + } + + /** + * Sets and returns the implicit wait time in seconds that is used by all + * Selenium 2 WebDriver instances. This affects all currently open and from + * now on opened instances.
+ *
+ * From selenium 2 function: Sets a sticky timeout to implicitly wait for + * an element to be found, or a command to complete. This method only needs + * to be called one time per session.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
${orig wait} =Set Selenium Implicit Wait10 seconds
# Perform AJAX call that is slow
Set Selenium Implicit Wait${orig wait}# Reset to old value
+ * + * @param timestr + * The implicit wait time in seconds. + * @return The previous implicit wait time in seconds. + * + * @see BrowserManagement#setBrowserImplicitWait + * @see BrowserManagement#getSeleniumImplicitWait + */ + @RobotKeyword + @ArgumentNames({ "timestr" }) public String setSeleniumImplicitWait(String timestr) { String oldWait = getSeleniumTimeout(); implicitWait = Robotframework.timestrToSecs(timestr); - for (SessionIdAliasWebDriverTuple sessionIdAliasWebDriverTuple : webDriverCache - .getWebDrivers()) { - sessionIdAliasWebDriverTuple.webDriver - .manage() - .timeouts() - .implicitlyWait((int) (implicitWait * 1000.0), - TimeUnit.MILLISECONDS); + for (SessionIdAliasWebDriverTuple sessionIdAliasWebDriverTuple : webDriverCache.getWebDrivers()) { + sessionIdAliasWebDriverTuple.webDriver.manage().timeouts() + .implicitlyWait((int) (implicitWait * 1000.0), TimeUnit.MILLISECONDS); } return oldWait; } + /** + * Sets and returns the implicit wait time in seconds that is used by the + * current Selenium 2 WebDriver instance.
+ *
+ * From selenium 2 function: Sets a sticky timeout to implicitly wait for + * an element to be found, or a command to complete. This method only needs + * to be called one time per session.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
${orig wait} =Set Browser Implicit Wait10 seconds
# Perform AJAX call that is slow
Set Browser Implicit Wait${orig wait}# Reset to old value
+ * + * @param timestr + * The implicit wait time in seconds. + * @return The previous implicit wait time in seconds. + * + * @see BrowserManagement#setSeleniumImplicitWait + */ + @RobotKeyword + @ArgumentNames({ "timestr" }) public String setBrowserImplicitWait(String timestr) { String oldWait = getSeleniumTimeout(); implicitWait = Robotframework.timestrToSecs(timestr); - webDriverCache - .getCurrent() - .manage() - .timeouts() - .implicitlyWait((int) (implicitWait * 1000.0), - TimeUnit.MILLISECONDS); + webDriverCache.getCurrent().manage().timeouts() + .implicitlyWait((int) (implicitWait * 1000.0), TimeUnit.MILLISECONDS); return oldWait; } + @RobotKeywordOverload public void setRemoteWebDriverProxy(String host, String port) { - setRemoteWebDriverProxy(host, port, "", "", "", ""); + setRemoteWebDriverProxy(host, port, ""); + } + + @RobotKeywordOverload + public void setRemoteWebDriverProxy(String host, String port, String user) { + setRemoteWebDriverProxy(host, port, user, ""); } - public void setRemoteWebDriverProxy(String host, String port, String user, - String password) { - setRemoteWebDriverProxy(host, port, user, password, "", ""); + @RobotKeywordOverload + public void setRemoteWebDriverProxy(String host, String port, String user, String password) { + setRemoteWebDriverProxy(host, port, user, password, ""); } - public void setRemoteWebDriverProxy(String host, String port, String user, - String password, String domain, String workstation) { + @RobotKeywordOverload + public void setRemoteWebDriverProxy(String host, String port, String user, String password, String domain) { + setRemoteWebDriverProxy(host, port, user, password, domain, ""); + } + + /** + * Configures proxy handling for remote WebDriver instances.
+ *
+ * This is needed to connect to an external Selenium 2 WebDriver rid through + * a local HTTP proxy. This implementation handles BASIC, DIGEST and NTLM + * based authentication schemes correctly.
+ *
+ * The given configuration will be used for all subsequent calls of `Open + * Browser`. To remove the proxy call:
+ * Set Remote Web Driver Proxy | ${EMPTY} | ${EMPTY}
+ *
+ * Some additional info: + *
    + *
  • If no username is provided, it looks for a username at the + * Java property http.proxyUser and the environment variables HTTP_PROXY and + * http_proxy. If a username is found, it is only used, if the host and port + * also match.
  • + *
  • If no password is provided, it looks for a password at the + * Java property http.proxyUser and the environment variables HTTP_PROXY and + * http_proxy. If a password is found, it is only used, if the host, port + * and username also match.
  • + *
  • If a domain is provided, NTLM based authentication is used
  • + *
  • If no workstation is provided and NTLM based authentication is + * used, the hostname is used as workstation name.
  • + *
+ * + * @param host + * The hostname of the proxy + * @param port + * The port of the proxy + * @param username + * Default=NONE. The usename + * @param password + * Default=NONE. The password of the user + * @param domain + * Default=NONE. The NTLM domain name + * @param workstation + * Default=NONE. The name of the workstation + */ + @RobotKeyword + @ArgumentNames({ "host", "port", "username=NONE", "password=NONE", "domain=NONE", "workstation=NONE" }) + public void setRemoteWebDriverProxy(String host, String port, String username, String password, String domain, + String workstation) { if (host.length() == 0 || port.length() == 0) { // No host and port given as proxy @@ -367,17 +1274,17 @@ public void setRemoteWebDriverProxy(String host, String port, String user, throw new Selenium2LibraryNonFatalException(e.getMessage()); } - if (user.length() == 0) { + if (username.length() == 0) { // look for a username from properties if (System.getProperty("http.proxyHost", "").equals(host) && System.getProperty("http.proxyPort", "").equals(port)) { - user = System.getProperty("http.proxyUser", ""); + username = System.getProperty("http.proxyUser", ""); } // look for a username from environment - if (user.length() == 0) { + if (username.length() == 0) { if (proxyUrl != null && proxyUrl.getHost().equals(host) && Integer.toString(proxyUrl.getPort()).equals(port)) { - user = getUserFromURL(proxyUrl); + username = getUserFromURL(proxyUrl); } } } @@ -386,14 +1293,14 @@ public void setRemoteWebDriverProxy(String host, String port, String user, // look for a password from properties if (System.getProperty("http.proxyHost", "").equals(host) && System.getProperty("http.proxyPort", "").equals(port) - && System.getProperty("http.proxyUser", "").equals(user)) { + && System.getProperty("http.proxyUser", "").equals(username)) { password = System.getProperty("http.proxyPassword", ""); } // look for a password from environment if (password.length() == 0) { if (proxyUrl != null && proxyUrl.getHost().equals(host) && Integer.toString(proxyUrl.getPort()).equals(port) - && getUserFromURL(proxyUrl).equals(user)) { + && getUserFromURL(proxyUrl).equals(username)) { password = getPasswordFromURL(proxyUrl); } } @@ -401,16 +1308,15 @@ && getUserFromURL(proxyUrl).equals(user)) { if (domain.length() != 0 && workstation.length() == 0) { try { - workstation = InetAddress.getLocalHost().getHostName() - .split("\\.")[0]; + workstation = InetAddress.getLocalHost().getHostName().split("\\.")[0]; } catch (UnknownHostException e) { - warn("No workstation name found"); + logging.warn("No workstation name found"); } } remoteWebDriverProxyHost = host; remoteWebDriverProxyPort = port; - remoteWebDriverProxyUser = user; + remoteWebDriverProxyUser = username; remoteWebDriverProxyPassword = password; remoteWebDriverProxyDomain = domain; remoteWebDriverProxyWorkstation = workstation; @@ -438,45 +1344,31 @@ protected String getPasswordFromURL(URL url) { return auth.substring(index + 1); } - protected WebDriver createWebDriver(String browserName, - String desiredCapabilitiesString, String profileDirectory, - String remoteUrlString) throws MalformedURLException { + protected WebDriver createWebDriver(String browserName, String desiredCapabilitiesString, String remoteUrlString, + String browserOptions) throws MalformedURLException { browserName = browserName.toLowerCase().replace(" ", ""); - DesiredCapabilities desiredCapabilities = createDesiredCapabilities( - browserName, desiredCapabilitiesString, profileDirectory); + DesiredCapabilities desiredCapabilities = createDesiredCapabilities(browserName, desiredCapabilitiesString, + browserOptions); WebDriver webDriver; if (remoteUrlString != null && !"False".equals(remoteUrlString)) { - webDriver = createRemoteWebDriver(desiredCapabilities, new URL( - remoteUrlString)); + webDriver = createRemoteWebDriver(desiredCapabilities, new URL(remoteUrlString)); } else { webDriver = createLocalWebDriver(browserName, desiredCapabilities); } - webDriver - .manage() - .timeouts() - .setScriptTimeout((int) (timeout * 1000.0), - TimeUnit.MILLISECONDS); - webDriver - .manage() - .timeouts() - .implicitlyWait((int) (implicitWait * 1000.0), - TimeUnit.MILLISECONDS); + webDriver.manage().timeouts().setScriptTimeout((int) (timeout * 1000.0), TimeUnit.MILLISECONDS); + webDriver.manage().timeouts().implicitlyWait((int) (implicitWait * 1000.0), TimeUnit.MILLISECONDS); return webDriver; } - protected WebDriver createLocalWebDriver(String browserName, - DesiredCapabilities desiredCapabilities) { + protected WebDriver createLocalWebDriver(String browserName, DesiredCapabilities desiredCapabilities) { if ("ff".equals(browserName) || "firefox".equals(browserName)) { return new FirefoxDriver(desiredCapabilities); - - } else if ("ie".equals(browserName) - || "internetexplorer".equals(browserName)) { + } else if ("ie".equals(browserName) || "internetexplorer".equals(browserName)) { return new InternetExplorerDriver(desiredCapabilities); - } else if ("gc".equals(browserName) || "chrome".equals(browserName) - || "googlechrome".equals(browserName)) { + } else if ("gc".equals(browserName) || "chrome".equals(browserName) || "googlechrome".equals(browserName)) { return new ChromeDriver(desiredCapabilities); } else if ("opera".equals(browserName)) { return new OperaDriver(desiredCapabilities); @@ -492,44 +1384,36 @@ protected WebDriver createLocalWebDriver(String browserName, return driver; } else if ("iphone".equals(browserName) || "ipad".equals(browserName)) { try { - return new IPhoneDriver(desiredCapabilities); + return new IOSDriver(new URL(""), desiredCapabilities); } catch (Exception e) { - throw new Selenium2LibraryFatalException("Creating " - + browserName + " instance failed.", e); + throw new Selenium2LibraryFatalException("Creating " + browserName + " instance failed.", e); } } else if ("android".equals(browserName)) { - return new AndroidDriver(desiredCapabilities); + try { + return new SelendroidDriver(desiredCapabilities); + } catch (Exception e) { + throw new Selenium2LibraryFatalException(e); + } } - throw new Selenium2LibraryFatalException(browserName - + " is not a supported browser."); + throw new Selenium2LibraryFatalException(browserName + " is not a supported browser."); } - protected WebDriver createRemoteWebDriver( - DesiredCapabilities desiredCapabilities, URL remoteUrl) { - HttpCommandExecutor httpCommandExecutor = new HttpCommandExecutor( - remoteUrl); + protected WebDriver createRemoteWebDriver(DesiredCapabilities desiredCapabilities, URL remoteUrl) { + HttpCommandExecutor httpCommandExecutor = new HttpCommandExecutor(remoteUrl); setRemoteWebDriverProxy(httpCommandExecutor); - return new Augmenter().augment(new RemoteWebDriver(httpCommandExecutor, - desiredCapabilities)); + return new Augmenter().augment(new RemoteWebDriver(httpCommandExecutor, desiredCapabilities)); } - protected DesiredCapabilities createDesiredCapabilities(String browserName, - String desiredCapabilitiesString, String profileDirectory) { + protected DesiredCapabilities createDesiredCapabilities(String browserName, String desiredCapabilitiesString, + String browserOptions) { DesiredCapabilities desiredCapabilities; if ("ff".equals(browserName) || "firefox".equals(browserName)) { desiredCapabilities = DesiredCapabilities.firefox(); - if (profileDirectory != null) { - FirefoxProfile profile = new FirefoxProfile(new File( - profileDirectory)); - desiredCapabilities.setCapability(FirefoxDriver.PROFILE, - profile); - } - } else if ("ie".equals(browserName) - || "internetexplorer".equals(browserName)) { + parseBrowserOptionsFirefox(browserOptions, desiredCapabilities); + } else if ("ie".equals(browserName) || "internetexplorer".equals(browserName)) { desiredCapabilities = DesiredCapabilities.internetExplorer(); - } else if ("gc".equals(browserName) || "chrome".equals(browserName) - || "googlechrome".equals(browserName)) { + } else if ("gc".equals(browserName) || "chrome".equals(browserName) || "googlechrome".equals(browserName)) { desiredCapabilities = DesiredCapabilities.chrome(); } else if ("opera".equals(browserName)) { desiredCapabilities = DesiredCapabilities.opera(); @@ -543,26 +1427,87 @@ protected DesiredCapabilities createDesiredCapabilities(String browserName, desiredCapabilities = DesiredCapabilities.iphone(); } else if ("android".equals(browserName)) { desiredCapabilities = DesiredCapabilities.android(); - } else if ("htmlunit".equals(browserName) - || "htmlunitwithjs".equals(browserName)) { + } else if ("htmlunit".equals(browserName) || "htmlunitwithjs".equals(browserName)) { desiredCapabilities = DesiredCapabilities.htmlUnit(); } else { - throw new Selenium2LibraryFatalException(browserName - + " is not a supported browser."); + throw new Selenium2LibraryFatalException(browserName + " is not a supported browser."); } - if (desiredCapabilitiesString != null - && !"None".equals(desiredCapabilitiesString)) { - for (String capability : desiredCapabilitiesString.split(",")) { - String[] keyValue = capability.split(":"); - desiredCapabilities.setCapability(keyValue[0], keyValue[1]); + if (desiredCapabilitiesString != null && !"None".equals(desiredCapabilitiesString)) { + JSONObject jsonObject = (JSONObject) JSONValue.parse(desiredCapabilitiesString); + if (jsonObject != null) { + // Valid JSON + Iterator iterator = jsonObject.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + desiredCapabilities.setCapability(entry.getKey().toString(), entry.getValue()); + } + } else { + // Invalid JSON. Old style key-value pairs + for (String capability : desiredCapabilitiesString.split(",")) { + String[] keyValue = capability.split(":"); + if (keyValue.length == 2) { + desiredCapabilities.setCapability(keyValue[0], keyValue[1]); + } else { + logging.warn("Invalid desiredCapabilities: " + desiredCapabilitiesString); + } + } } } return desiredCapabilities; } - protected void setRemoteWebDriverProxy( - HttpCommandExecutor httpCommandExecutor) { + protected void parseBrowserOptionsFirefox(String browserOptions, DesiredCapabilities desiredCapabilities) { + if (browserOptions != null && !"NONE".equals(browserOptions)) { + JSONObject jsonObject = (JSONObject) JSONValue.parse(browserOptions); + if (jsonObject != null) { + FirefoxProfile firefoxProfile = new FirefoxProfile(); + Iterator iterator = jsonObject.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + String key = entry.getKey().toString(); + if (key.equals("preferences")) { + // Preferences + JSONObject preferences = (JSONObject) entry.getValue(); + Iterator iteratorPreferences = preferences.entrySet().iterator(); + while (iteratorPreferences.hasNext()) { + Entry entryPreferences = (Entry) iteratorPreferences.next(); + Object valuePreferences = entryPreferences.getValue(); + if (valuePreferences instanceof Number) { + firefoxProfile.setPreference(entryPreferences.getKey().toString(), + ((Number) valuePreferences).intValue()); + } else if (valuePreferences instanceof Boolean) { + firefoxProfile.setPreference(entryPreferences.getKey().toString(), + ((Boolean) valuePreferences).booleanValue()); + } else { + firefoxProfile.setPreference(entryPreferences.getKey().toString(), + valuePreferences.toString()); + } + } + } else if (key.equals("extensions")) { + // Extensions + JSONArray extensions = (JSONArray) entry.getValue(); + Iterator iteratorExtensions = extensions.iterator(); + while (iteratorExtensions.hasNext()) { + File file = new File(iteratorExtensions.next().toString().replace('/', File.separatorChar)); + try { + firefoxProfile.addExtension(file); + } catch (IOException e) { + logging.warn("Could not load extension: " + file.getAbsolutePath()); + } + } + } else { + logging.warn("Unknown browserOption: " + key + ":" + entry.getValue()); + } + } + desiredCapabilities.setCapability(FirefoxDriver.PROFILE, firefoxProfile); + } else { + logging.warn("Invalid browserOptions: " + browserOptions); + } + } + } + + protected void setRemoteWebDriverProxy(HttpCommandExecutor httpCommandExecutor) { if (remoteWebDriverProxyHost.length() == 0) { return; } @@ -575,86 +1520,55 @@ protected void setRemoteWebDriverProxy( className = "DefaultHttpClient"; Field field = HttpCommandExecutor.class.getDeclaredField(fieldName); field.setAccessible(true); - DefaultHttpClient client = (DefaultHttpClient) field - .get(httpCommandExecutor); + DefaultHttpClient client = (DefaultHttpClient) field.get(httpCommandExecutor); // set the credentials for the proxy - AuthScope authScope = new AuthScope(remoteWebDriverProxyHost, - Integer.parseInt(remoteWebDriverProxyPort)); + AuthScope authScope = new AuthScope(remoteWebDriverProxyHost, Integer.parseInt(remoteWebDriverProxyPort)); if (remoteWebDriverProxyDomain.length() == 0) { // BASIC Authentication - client.getCredentialsProvider().setCredentials( - authScope, - new UsernamePasswordCredentials( - remoteWebDriverProxyUser, - remoteWebDriverProxyPassword)); + client.getCredentialsProvider().setCredentials(authScope, + new UsernamePasswordCredentials(remoteWebDriverProxyUser, remoteWebDriverProxyPassword)); } else { // NTLM Authentication client.getCredentialsProvider().setCredentials( authScope, - new NTCredentials(remoteWebDriverProxyUser, - remoteWebDriverProxyPassword, - remoteWebDriverProxyWorkstation, - remoteWebDriverProxyDomain)); + new NTCredentials(remoteWebDriverProxyUser, remoteWebDriverProxyPassword, + remoteWebDriverProxyWorkstation, remoteWebDriverProxyDomain)); } // Set the RoutePlanner back to something that handles // proxies correctly. - client.setRoutePlanner(new DefaultHttpRoutePlanner(client - .getConnectionManager().getSchemeRegistry())); - HttpHost proxy = new HttpHost(remoteWebDriverProxyHost, - Integer.parseInt(remoteWebDriverProxyPort)); - client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, - proxy); + client.setRoutePlanner(new DefaultHttpRoutePlanner(client.getConnectionManager().getSchemeRegistry())); + HttpHost proxy = new HttpHost(remoteWebDriverProxyHost, Integer.parseInt(remoteWebDriverProxyPort)); + client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); } catch (SecurityException e) { - throw new Selenium2LibraryFatalException( - String.format( - "The SecurityManager does not allow us to lookup to the %s field.", - fieldName)); + throw new Selenium2LibraryFatalException(String.format( + "The SecurityManager does not allow us to lookup to the %s field.", fieldName)); } catch (NoSuchFieldException e) { - throw new Selenium2LibraryFatalException( - String.format( - "The RemoteWebDriver dose not declare the %s field any more.", - fieldName)); - } catch (IllegalArgumentException e) { throw new Selenium2LibraryFatalException(String.format( - "The field %s does not belong to the given object.", + "The RemoteWebDriver dose not declare the %s field any more.", fieldName)); + } catch (IllegalArgumentException e) { + throw new Selenium2LibraryFatalException(String.format("The field %s does not belong to the given object.", fieldName)); } catch (IllegalAccessException e) { - throw new Selenium2LibraryFatalException( - String.format( - "The SecurityManager does not allow us to access to the %s field.", - fieldName)); + throw new Selenium2LibraryFatalException(String.format( + "The SecurityManager does not allow us to access to the %s field.", fieldName)); } catch (ClassCastException e) { - throw new Selenium2LibraryFatalException( - String.format("The %s field does not contain a %s.", - fieldName, className)); + throw new Selenium2LibraryFatalException(String.format("The %s field does not contain a %s.", fieldName, + className)); } - } - // ############################## - // Forward Declarations - // ############################## - - protected abstract List elementFind(String locator, - boolean firstOnly, boolean required); - - protected abstract void log(String msg, String logLevel); - - protected abstract void trace(String msg); - - protected abstract void debug(String msg); - - protected abstract void info(String msg); - - protected abstract void html(String msg); - - protected abstract void warn(String msg); - - protected abstract List logList(List items); - - protected abstract List logList(List items, String what); + protected List toList(List items) { + return toList(items, "item"); + } - protected abstract File getLogDir(); + protected List toList(List items, String what) { + List msg = new ArrayList(); + msg.add(String.format("Altogether %d %s%s.\n", items.size(), what, items.size() == 1 ? "" : "s")); + for (int index = 0; index < items.size(); index++) { + msg.add(String.format("%d: %s", index + 1, items.get(index))); + } + return items; + } } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Cookie.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Cookie.java index 6fcf005..2805d89 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Cookie.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Cookie.java @@ -2,30 +2,63 @@ import java.util.ArrayList; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywordOverload; +import org.robotframework.javalib.annotation.RobotKeywords; + +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; -public abstract class Cookie extends BrowserManagement { +@RobotKeywords +public class Cookie extends RunOnFailureKeywordsAdapter { + + /** + * Instantiated BrowserManagement keyword bean + */ + @Autowired + protected BrowserManagement browserManagement; // ############################## // Keywords // ############################## + /** + * Deletes all cookies.
+ */ + @RobotKeyword public void deleteAllCookies() { - webDriverCache.getCurrent().manage().deleteAllCookies(); + browserManagement.getCurrentWebDriver().manage().deleteAllCookies(); } + /** + * Deletes cookie matching name.
+ *
+ * If the cookie is not found, nothing happens
+ * + * @param name + * The name of the cookie to delete. + */ + @RobotKeyword + @ArgumentNames({ "name" }) public void deleteCookie(String name) { - webDriverCache.getCurrent().manage().deleteCookieNamed(name); + browserManagement.getCurrentWebDriver().manage().deleteCookieNamed(name); } + /** + * Returns all cookies of the current page.
+ * + * @return All cookies of the current page. + */ + @RobotKeyword public String getCookies() { StringBuffer ret = new StringBuffer(); - ArrayList cookies = new ArrayList( - webDriverCache.getCurrent().manage().getCookies()); + ArrayList cookies = new ArrayList(browserManagement + .getCurrentWebDriver().manage().getCookies()); for (int i = 0; i < cookies.size(); i++) { - ret.append(cookies.get(i).getName() + "=" - + cookies.get(i).getValue()); + ret.append(cookies.get(i).getName() + "=" + cookies.get(i).getValue()); if (i != cookies.size() - 1) { ret.append("; "); } @@ -34,23 +67,70 @@ public String getCookies() { return ret.toString(); } + /** + * Returns value of cookie found with name.
+ *
+ * If no cookie is found with name, this keyword fails.
+ * + * @param name + * The name of the cookie + * @return The value of the found cookie + */ + @RobotKeyword + @ArgumentNames({ "name" }) public String getCookieValue(String name) { - org.openqa.selenium.Cookie cookie = webDriverCache.getCurrent() - .manage().getCookieNamed(name); + org.openqa.selenium.Cookie cookie = browserManagement.getCurrentWebDriver().manage().getCookieNamed(name); if (cookie != null) { return cookie.getValue(); } else { - throw new Selenium2LibraryNonFatalException(String.format( - "Cookie with name %s not found.", name)); + throw new Selenium2LibraryNonFatalException(String.format("Cookie with name %s not found.", name)); } } - public void addCookie(String name, String value, String path, - String domain, String secure, String expiry) { + @RobotKeywordOverload + public void addCookie(String name, String value) { + addCookie(name, value, null); + } + + @RobotKeywordOverload + public void addCookie(String name, String value, String path) { + addCookie(name, value, path, null); + } + + @RobotKeywordOverload + public void addCookie(String name, String value, String path, String domain) { + addCookie(name, value, path, domain, ""); + } + + @RobotKeywordOverload + public void addCookie(String name, String value, String path, String domain, String secure) { + addCookie(name, value, path, domain, secure, null); + } + + /** + * Adds a cookie to your current session.
+ * + * @param name + * The name of the cookie. + * @param value + * The cookie value. + * @param path + * Default=NONE. The path the cookie is visible to. + * @param domain + * Default=NONE. The domain the cookie is visible to. + * @param secure + * Default=NONE. Whether this cookie requires a secure + * connection. + * @param expiry + * Default=NONE. The cookie's expiration date + */ + @RobotKeyword + @ArgumentNames({ "name", "value", "path=NONE", "domain=NONE", "secure=NONE", "expiry=NONE" }) + public void addCookie(String name, String value, String path, String domain, String secure, String expiry) { // Parameter expiry not used by Python library - org.openqa.selenium.Cookie cookie = new org.openqa.selenium.Cookie( - name, value, domain, path, null, secure.equals("True")); - webDriverCache.getCurrent().manage().addCookie(cookie); + org.openqa.selenium.Cookie cookie = new org.openqa.selenium.Cookie(name, value, domain, path, null, + "true".equals(secure.toLowerCase())); + browserManagement.getCurrentWebDriver().manage().addCookie(cookie); } } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Element.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Element.java index 42f177e..00787fd 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Element.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Element.java @@ -11,279 +11,840 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywordOverload; +import org.robotframework.javalib.annotation.RobotKeywords; +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; import com.github.markusbernhardt.selenium2library.locators.ElementFinder; import com.github.markusbernhardt.selenium2library.utils.Python; -public abstract class Element extends Cookie { +@RobotKeywords +public class Element extends RunOnFailureKeywordsAdapter { + + /** + * Instantiated BrowserManagement keyword bean + */ + @Autowired + protected BrowserManagement browserManagement; + + /** + * Instantiated FormElement keyword bean + */ + @Autowired + protected FormElement formElement; + + /** + * Instantiated Logging keyword bean + */ + @Autowired + protected Logging logging; // ############################## // Keywords - Element Lookups // ############################## + @RobotKeywordOverload + @ArgumentNames({ "text" }) public void currentFrameContains(String text) { - this.currentFrameContains(text, "INFO"); - } - + currentFrameContains(text, "INFO"); + } + + /** + * Verify the current frame contains text.
+ *
+ * See `Introduction` for details about log levels.
+ * + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "text", "logLevel=INFO" }) public void currentFrameContains(String text, String logLevel) { if (!isTextPresent(text)) { - log(String.format("Current Frame Contains: %s => FAILED", text), - logLevel); + logging.log(String.format("Current Frame Contains: %s => FAILED", text), logLevel); throw new Selenium2LibraryNonFatalException(String.format( "Page should have contained text '%s', but did not.", text)); } else { - log(String.format("Current Frame Contains: %s => OK", text), - logLevel); + logging.log(String.format("Current Frame Contains: %s => OK", text), logLevel); } } + @RobotKeywordOverload public void currentFrameShouldNotContain(String text) { - this.currentFrameShouldNotContain(text, "INFO"); - } - + currentFrameShouldNotContain(text, "INFO"); + } + + /** + * Verify the current frame does not contain text.
+ *
+ * See `Introduction` for details about log levels.
+ * + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "text", "logLevel=INFO" }) public void currentFrameShouldNotContain(String text, String logLevel) { if (isTextPresent(text)) { - log(String.format("Current Frame Should Not Contain: %s => FAILED", text), - logLevel); + logging.log(String.format("Current Frame Should Not Contain: %s => FAILED", text), logLevel); throw new Selenium2LibraryNonFatalException(String.format( "Page should have not contained text '%s', but did.", text)); } else { - log(String.format("Current Frame Should Not Contain: %s => OK", text), - logLevel); + logging.log(String.format("Current Frame Should Not Contain: %s => OK", text), logLevel); } } - public void elementShouldContain(String locator, String expected) { - this.elementShouldContain(locator, expected, ""); + @RobotKeywordOverload + public void elementShouldContain(String locator, String text) { + elementShouldContain(locator, text, ""); + } + + /** + * Verify the element identified by locator contains text.
+ *
+ * See `Introduction` for details about locators. + * + * @param locator + * The locator to locate the element. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void elementShouldContain(String locator, String text, String message) { + String actual = getText(locator); + + if (!actual.toLowerCase().contains(text.toLowerCase())) { + logging.info(String.format("Element Should Contain: %s => FAILED", text)); + throw new Selenium2LibraryNonFatalException(String.format( + "Element should have contained text '%s', but its text was %s.", text, actual)); + } else { + logging.info(String.format("Element Should Contain: %s => OK", text)); + } } - public void elementShouldContain(String locator, String expected, - String message) { - String actual = fetchText(locator); - - if (!actual.toLowerCase().contains(expected.toLowerCase())) { - info(String - .format("Element Should Contain: %s => FAILED", expected)); - throw new Selenium2LibraryNonFatalException( - String.format( - "Element should have contained text '%s' but its text was %s.", - expected, actual)); + @RobotKeywordOverload + public void elementShouldNotContain(String locator, String text) { + elementShouldNotContain(locator, text, ""); + } + + /** + * Verify the element identified by locator does not contain + * text.
+ *
+ * See `Introduction` for details about locators. + * + * @param locator + * The locator to locate the element. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void elementShouldNotContain(String locator, String text, String message) { + String actual = getText(locator); + + if (actual.toLowerCase().contains(text.toLowerCase())) { + logging.info(String.format("Element Should Not Contain: %s => FAILED", text)); + throw new Selenium2LibraryNonFatalException(String.format( + "Element should not have contained text '%s', but its text was %s.", text, actual)); } else { - info(String.format("Element Should Contain: %s => OK", expected)); + logging.info(String.format("Element Should Not Contain: %s => OK", text)); } } + @RobotKeywordOverload public void frameShouldContain(String locator, String text) { - this.frameShouldContain(locator, text, "INFO"); - } - + frameShouldContain(locator, text, "INFO"); + } + + /** + * Verify the frame identified by locator contains text.
+ *
+ * See `Introduction` for details about locators. + * + * @param locator + * The locator to locate the frame. + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "logLevel=INFO" }) public void frameShouldContain(String locator, String text, String logLevel) { if (!frameContains(locator, text)) { - log(String.format("Frame Should Contain: %s => FAILED", text), - logLevel); + logging.log(String.format("Frame Should Contain: %s => FAILED", text), logLevel); throw new Selenium2LibraryNonFatalException(String.format( - "Frame should have contained text '%s' but did not.", text)); + "Frame should have contained text '%s', but did not.", text)); } else { - log(String.format("Frame Should Contain: %s => OK", text), logLevel); + logging.log(String.format("Frame Should Contain: %s => OK", text), logLevel); } } - public void pageShouldContain(String text) { - this.pageShouldContain(text, "INFO"); + /** + * Verify the frame identified by locator does not contain + * text.
+ *
+ * See `Introduction` for details about locators. + * + * @param locator + * The locator to locate the frame. + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "logLevel=INFO" }) + public void frameShouldNotContain(String locator, String text, String logLevel) { + if (frameContains(locator, text)) { + logging.log(String.format("Frame Should Not Contain: %s => FAILED", text), logLevel); + throw new Selenium2LibraryNonFatalException(String.format( + "Frame should not have contained text '%s', but did.", text)); + } else { + logging.log(String.format("Frame Should Not Contain: %s => OK", text), logLevel); + } } + @RobotKeywordOverload + public void pageShouldContain(String text) { + pageShouldContain(text, "INFO"); + } + + /** + * Verify the current page contains text.
+ *
+ * See `Introduction` for details about log levels.
+ * + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "text", "logLevel=INFO" }) public void pageShouldContain(String text, String logLevel) { if (!pageContains(text)) { - log(String.format("Page Should Contain: %s => FAILED", text), - logLevel); + logging.log(String.format("Page Should Contain: %s => FAILED", text), logLevel); throw new Selenium2LibraryNonFatalException(String.format( "Page should have contained text '%s' but did not.", text)); } else { - log(String.format("Page Should Contain: %s => OK", text), logLevel); + logging.log(String.format("Page Should Contain: %s => OK", text), logLevel); } } - public void pageShouldContainElement(String locator) { - this.pageShouldContainElement(locator, "", "INFO"); - } - - public void pageShouldContainElement(String locator, String message) { - this.pageShouldContainElement(locator, message, "INFO"); - } - - public void pageShouldContainElement(String locator, String message, - String logLevel) { - this.pageShouldContainElement(locator, null, message, "INFO"); - } - - public void pageShouldContainElement(String locator, String tag, - String message, String logLevel) { - helperPageShouldContainElement(locator, tag, message, logLevel); - } - + @RobotKeywordOverload public void pageShouldNotContain(String text) { - this.pageShouldNotContain(text, "INFO"); - } - + pageShouldNotContain(text, "INFO"); + } + + /** + * Verify the current page does not contain text.
+ *
+ * See `Introduction` for details about log levels.
+ * + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "text", "logLevel=INFO" }) public void pageShouldNotContain(String text, String logLevel) { if (pageContains(text)) { - log(String.format("Page Should Not Contain: %s => FAILED", text), - logLevel); + logging.log(String.format("Page Should Not Contain: %s => FAILED", text), logLevel); throw new Selenium2LibraryNonFatalException(String.format( "Page should not have contained text '%s' but did.", text)); } else { - log(String.format("Page Should Not Contain: %s => OK", text), - logLevel); + logging.log(String.format("Page Should Not Contain: %s => OK", text), logLevel); } } - public void pageShouldNotContainElement(String locator) { - this.pageShouldNotContainElement(locator, "", "INFO"); + @RobotKeywordOverload + public void pageShouldContainElement(String locator) { + pageShouldContainElement(locator, "", "INFO"); } - public void pageShouldNotContainElement(String locator, String message) { - this.pageShouldNotContainElement(locator, message, "INFO"); + @RobotKeywordOverload + public void pageShouldContainElement(String locator, String message) { + pageShouldContainElement(locator, message, "INFO"); + } + + /** + * Verify the element identified by locator is found on the current + * page.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about log levels and locators.
+ * + * @param locator + * The locator to locate the element. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldContainElement(String locator, String message, String logLevel) { + pageShouldContainElement(locator, null, message, "INFO"); + } + + protected void pageShouldContainElement(String locator, String tag, String message, String logLevel) { + String name = tag != null ? tag : "element"; + if (!isElementPresent(locator, tag)) { + if (message == null || message.equals("")) { + message = String.format("Page should have contained %s '%s' but did not", name, locator); + } + logging.log(message, logLevel); + throw new Selenium2LibraryNonFatalException(message); + } else { + logging.log(String.format("Current page contains %s '%s'.", name, locator), logLevel); + } } - public void pageShouldNotContainElement(String locator, String message, - String logLevel) { - this.pageShouldNotContainElement(locator, null, message, "INFO"); + @RobotKeywordOverload + public void pageShouldNotContainElement(String locator) { + pageShouldNotContainElement(locator, "", "INFO"); } - public void pageShouldNotContainElement(String locator, String tag, - String message, String logLevel) { - helperPageShouldNotContainElement(locator, tag, message, logLevel); + @RobotKeywordOverload + public void pageShouldNotContainElement(String locator, String message) { + pageShouldNotContainElement(locator, message, "INFO"); + } + + /** + * Verify the element identified by locator is not found on the + * current page.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about log levels and locators.
+ * + * @param locator + * The locator to locate the element. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldNotContainElement(String locator, String message, String logLevel) { + pageShouldNotContainElement(locator, null, message, "INFO"); + } + + protected void pageShouldNotContainElement(String locator, String tag, String message, String logLevel) { + String name = tag != null ? tag : "element"; + if (isElementPresent(locator, tag)) { + if (message == null || message.equals("")) { + message = String.format("Page should not have contained %s '%s' but did", name, locator); + } + logging.log(message, logLevel); + throw new Selenium2LibraryNonFatalException(message); + } else { + logging.log(String.format("Current page does not contain %s '%s'.", name, locator), logLevel); + } } // ############################## // Keywords - Attributes // ############################## + /** + * Assigns a temporary identifier to the element identified by + * locator
+ *
+ * This is mainly useful, when the locator is a complicated and slow XPath + * expression. The identifier expires when the page is reloaded.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + *
Assign ID to Elementxpath=//div[@id=\"first_div\"]my id
Page Should Contain Elementmy id
+ * + * @param locator + * The locator to locate the element. + * @param id + * The id to assign. + */ + @RobotKeyword + @ArgumentNames({ "locator", "id" }) public void assignIdToElement(String locator, String id) { - info(String.format("Assigning temporary id '%s' to element '%s'", id, - locator)); + logging.info(String.format("Assigning temporary id '%s' to element '%s'", id, locator)); List elements = elementFind(locator, true, true); - ((JavascriptExecutor) webDriverCache.getCurrent()).executeScript( + ((JavascriptExecutor) browserManagement.getCurrentWebDriver()).executeScript( String.format("arguments[0].id = '%s';", id), elements.get(0)); } + /** + * Verify the element identified by locator is enabled.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) + public void elementShouldBeEnabled(String locator) { + if (!isEnabled(locator)) { + throw new Selenium2LibraryNonFatalException(String.format("Element %s is disabled.", locator)); + } + } + + /** + * Verify the element identified by locator is disabled.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void elementShouldBeDisabled(String locator) { if (isEnabled(locator)) { - throw new Selenium2LibraryNonFatalException(String.format( - "Element %s is enabled.", locator)); + throw new Selenium2LibraryNonFatalException(String.format("Element %s is enabled.", locator)); } } - public void elementShouldBeEnabled(String locator) { - if (!isEnabled(locator)) { - throw new Selenium2LibraryNonFatalException(String.format( - "Element %s is disabled.", locator)); + @RobotKeywordOverload + public void elementShouldBeSelected(String locator) { + elementShouldBeSelected(locator, ""); + } + + /** + * Verify the element identified by locator is selected.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE" }) + public void elementShouldBeSelected(String locator, String message) { + logging.info(String.format("Verifying element '%s' is selected.", locator)); + boolean selected = isSelected(locator); + + if (!selected) { + if (message == null || message.equals("")) { + message = String.format("Element '%s' should be selected, but it is not.", locator); + } + throw new Selenium2LibraryNonFatalException(message); } } - public void elementShouldBeVisible(String locator) { - this.elementShouldBeVisible(locator, ""); + @RobotKeywordOverload + public void elementShouldNotBeSelected(String locator) { + elementShouldNotBeSelected(locator, ""); + } + + /** + * Verify the element identified by locator is not selected.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE" }) + public void elementShouldNotBeSelected(String locator, String message) { + logging.info(String.format("Verifying element '%s' is not selected.", locator)); + boolean selected = isSelected(locator); + + if (selected) { + if (message == null || message.equals("")) { + message = String.format("Element '%s' should not be selected, but it is.", locator); + } + throw new Selenium2LibraryNonFatalException(message); + } } + @RobotKeywordOverload + public void elementShouldBeVisible(String locator) { + elementShouldBeVisible(locator, ""); + } + + /** + * Verify the element identified by locator is visible.
+ *
+ * Herein, visible means that the element is logically visible, not + * optically visible in the current browser viewport. For example, an + * element that carries display:none is not logically visible, so using this + * keyword on that element would fail.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE" }) public void elementShouldBeVisible(String locator, String message) { - info(String.format("Verifying element '%s' is visible.", locator)); + logging.info(String.format("Verifying element '%s' is visible.", locator)); boolean visible = isVisible(locator); if (!visible) { if (message == null || message.equals("")) { - message = String.format( - "Element '%s' should be visible, but it is not.", - locator); + message = String.format("Element '%s' should be visible, but it is not.", locator); } throw new Selenium2LibraryNonFatalException(message); } } + @RobotKeywordOverload public void elementShouldNotBeVisible(String locator) { - this.elementShouldNotBeVisible(locator, ""); - } - + elementShouldNotBeVisible(locator, ""); + } + + /** + * Verify the element identified by locator is not visible.
+ *
+ * Herein, visible means that the element is logically visible, not + * optically visible in the current browser viewport. For example, an + * element that carries display:none is not logically visible, so using this + * keyword on that element would fail.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE" }) public void elementShouldNotBeVisible(String locator, String message) { - info(String.format("Verifying element '%s' is not visible.", locator)); + logging.info(String.format("Verifying element '%s' is not visible.", locator)); boolean visible = isVisible(locator); if (visible) { if (message == null || message.equals("")) { - message = String.format( - "Element '%s' should not be visible, but it is.", - locator); + message = String.format("Element '%s' should not be visible, but it is.", locator); } throw new Selenium2LibraryNonFatalException(message); } } - public void elementTextShouldBe(String locator, String expected) { - this.elementTextShouldBe(locator, expected, ""); + @RobotKeywordOverload + public void elementShouldBeClickable(String locator) { + elementShouldBeClickable(locator, ""); + } + + /** + * Verify the element identified by locator is clickable.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE" }) + public void elementShouldBeClickable(String locator, String message) { + logging.info(String.format("Verifying element '%s' is clickable.", locator)); + boolean clickable = isClickable(locator); + + if (!clickable) { + if (message == null || message.equals("")) { + message = String.format("Element '%s' should be clickable, but it is not.", locator); + } + throw new Selenium2LibraryNonFatalException(message); + } + } + + @RobotKeywordOverload + public void elementShouldNotBeClickable(String locator) { + elementShouldNotBeClickable(locator, ""); + } + + /** + * Verify the element identified by locator is not clickable.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE" }) + public void elementShouldNotBeClickable(String locator, String message) { + logging.info(String.format("Verifying element '%s' is not clickable.", locator)); + boolean clickable = isClickable(locator); + + if (clickable) { + if (message == null || message.equals("")) { + message = String.format("Element '%s' should not be clickable, but it is.", locator); + } + throw new Selenium2LibraryNonFatalException(message); + } } - public void elementTextShouldBe(String locator, String expected, - String message) { - info(String.format( - "Verifying element '%s' contains exactly text '%s'.", locator, - expected)); + @RobotKeywordOverload + public void elementTextShouldBe(String locator, String expected) { + elementTextShouldBe(locator, expected, ""); + } + + /** + * Verify the text of the element identified by locator is exactly + * text.
+ *
+ * In contrast to `Element Should Contain`, this keyword does not try a + * substring match but an exact match on the element identified by locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void elementTextShouldBe(String locator, String text, String message) { + List elements = elementFind(locator, true, true); + String actual = elements.get(0).getText(); + + if (!text.equals(actual)) { + if (message == null || message.equals("")) { + message = String.format("The text of element '%s' should have been '%s', but it was '%s'.", locator, + text, actual); + } + throw new Selenium2LibraryNonFatalException(message); + } + } + @RobotKeywordOverload + public void elementTextShouldNotBe(String locator, String expected) { + elementTextShouldNotBe(locator, expected, ""); + } + + /** + * Verify the text of the element identified by locator is not + * exactly text.
+ *
+ * In contrast to `Element Should Not Contain`, this keyword does not try a + * substring match but an exact match on the element identified by locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void elementTextShouldNotBe(String locator, String text, String message) { List elements = elementFind(locator, true, true); String actual = elements.get(0).getText(); - if (!expected.equals(actual)) { + if (text.equals(actual)) { if (message == null || message.equals("")) { - message = String - .format("The text of element '%s' should have been '%s', but it was '%s'.", - locator, expected, actual); + message = String.format("The text of element '%s' should have been '%s', but it was '%s'.", locator, + text, actual); } throw new Selenium2LibraryNonFatalException(message); } } + /** + * Returns the value of an element attribute.
+ *
+ * The attribute_locator consists of element locator followed by an @ + * sign and attribute name. Example: element_id@class
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param attributeLocator + * The attribute locator. + * @return The attribute value. + */ + @RobotKeyword + @ArgumentNames({ "attributeLocator" }) public String getElementAttribute(String attributeLocator) { String[] parts = parseAttributeLocator(attributeLocator); List elements = elementFind(parts[0], true, false); if (elements.size() == 0) { - throw new Selenium2LibraryNonFatalException(String.format( - "Element '%s' not found.", parts[0])); + throw new Selenium2LibraryNonFatalException(String.format("Element '%s' not found.", parts[0])); } return elements.get(0).getAttribute(parts[1]); } + /** + * Clears the text from element identified by locator.
+ *
+ * This keyword does not execute any checks on whether or not the clear + * method has succeeded, so if any subsequent checks are needed, they should + * be executed using method `Element Text Should Be`.
+ *
+ * Also, this method will use WebDriver's internal _element.clear()_ method, + * i.e. it will not send any keypresses, and it will not have any effect + * whatsoever on elements other than input textfields or input textareas. + * Clients relying on keypresses should implement their own methods.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) + public void clearElementText(String locator) { + List elements = elementFind(locator, true, true); + + elements.get(0).clear(); + } + + /** + * Returns horizontal position of element identified by locator.
+ *
+ * The position is returned in pixels off the left side of the page, as an + * integer. Fails if the matching element is not found.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @return The horizontal position + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public int getHorizontalPosition(String locator) { List elements = elementFind(locator, true, false); if (elements.size() == 0) { - throw new Selenium2LibraryNonFatalException(String.format( - "Could not determine position for '%s'.", locator)); + throw new Selenium2LibraryNonFatalException( + String.format("Could not determine position for '%s'.", locator)); } return elements.get(0).getLocation().getX(); } + /** + * Returns the value attribute of the element identified by locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @return The value attribute of the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public String getValue(String locator) { return getValue(locator, null); } - public String getValue(String locator, String tag) { - return this.fetchValue(locator, tag); + protected String getValue(String locator, String tag) { + List elements = elementFind(locator, true, false, tag); + + if (elements.size() == 0) { + return null; + } + + return elements.get(0).getAttribute("value"); } + /** + * Returns the text of the element identified by locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @return The text of the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public String getText(String locator) { - return fetchText(locator); + List elements = elementFind(locator, true, true); + + if (elements.size() == 0) { + return null; + } + + return elements.get(0).getText(); } + /** + * Returns vertical position of element identified by locator.
+ *
+ * The position is returned in pixels off the top of the page, as an + * integer. Fails if the matching element is not found.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @return The vertical position + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public int getVerticalPosition(String locator) { List elements = elementFind(locator, true, false); if (elements.size() == 0) { - throw new Selenium2LibraryNonFatalException(String.format( - "Could not determine position for '%s'.", locator)); + throw new Selenium2LibraryNonFatalException( + String.format("Could not determine position for '%s'.", locator)); } return elements.get(0).getLocation().getY(); @@ -293,64 +854,211 @@ public int getVerticalPosition(String locator) { // Keywords - Mouse Input/Events // ############################## + /** + * Click on the element identified by locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void clickElement(String locator) { - info(String.format("Clicking element '%s'.", locator)); + logging.info(String.format("Clicking element '%s'.", locator)); List elements = elementFind(locator, true, true); elements.get(0).click(); } + /** + * Click on the element identified by locator at the coordinates + * xOffset and yOffset.
+ *
+ * The cursor is moved at the center of the element and the to the given x/y + * offset from that point. Both offsets are specified as negative (left/up) + * or positive (right/down) number.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ *
+ * + * @param locator + * The locator to locate the element. + * @param xOffset + * The horizontal offset in pixel. Negative means left, positive + * right. + * @param yOffset + * The vertical offset in pixel. Negative means up, positive + * down. + */ + @RobotKeyword + @ArgumentNames({ "locator", "xOffset", "yOffset" }) + public void clickElementAtCoordinates(String locator, String xOffset, String yOffset) { + logging.info(String.format("Clicking element '%s'in coordinates '%s', '%s'.", locator, xOffset, yOffset)); + List elements = elementFind(locator, true, true); + + WebElement element = elements.get(0); + Actions action = new Actions(browserManagement.getCurrentWebDriver()); + action.moveToElement(element).moveByOffset(Integer.parseInt(xOffset), Integer.parseInt(yOffset)).perform(); + } + + /** + * Double-Click on the element identified by locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void doubleClickElement(String locator) { - info(String.format("Double clicking element '%s'.", locator)); + logging.info(String.format("Double clicking element '%s'.", locator)); List elements = elementFind(locator, true, true); - Actions action = new Actions(webDriverCache.getCurrent()); + Actions action = new Actions(browserManagement.getCurrentWebDriver()); action.doubleClick(elements.get(0)).perform(); } + /** + * Set the focus to the element identified by locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void focus(String locator) { List elements = elementFind(locator, true, true); - ((JavascriptExecutor) webDriverCache.getCurrent()).executeScript( - "arguments[0].focus();", elements.get(0)); - } - + ((JavascriptExecutor) browserManagement.getCurrentWebDriver()).executeScript("arguments[0].focus();", + elements.get(0)); + } + + /** + * Drag the element identified by the locator source and move it on + * top of the element identified by the locator target.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ *
+ * Example: + * + * + * + * + * + * + * + *
Drag And Dropelem1elem2# Move elem1 over elem2
+ * + * @param source + * The locator to locate the element to drag. + * @param target + * The locator to locate the element where to drop the dragged + * element. + */ + @RobotKeyword + @ArgumentNames({ "source", "target" }) public void dragAndDrop(String source, String target) { List sourceElements = elementFind(source, true, true); List targetElements = elementFind(target, true, true); - Actions action = new Actions(webDriverCache.getCurrent()); - action.dragAndDrop(sourceElements.get(0), targetElements.get(0)) - .perform(); - } - + Actions action = new Actions(browserManagement.getCurrentWebDriver()); + action.dragAndDrop(sourceElements.get(0), targetElements.get(0)).perform(); + } + + /** + * Drag the element identified by the locator source and move it by + * xOffset and yOffset.
+ *
+ * Both offsets are specified as negative (left/up) or positive (right/down) + * number.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ *
+ * Example: + * + * + * + * + * + * + * + * + *
Drag And Drop By Offsetelem15035# Move elem1 50px right and 35px down.
+ * + * @param source + * The locator to locate the element to drag. + * @param xOffset + * The horizontal offset in pixel. Negative means left, positive + * right. + * @param yOffset + * The vertical offset in pixel. Negative means up, positive + * down. + */ + @RobotKeyword + @ArgumentNames({ "source", "xOffset", "yOffset" }) public void dragAndDropByOffset(String source, int xOffset, int yOffset) { List elements = elementFind(source, true, true); - Actions action = new Actions(webDriverCache.getCurrent()); + Actions action = new Actions(browserManagement.getCurrentWebDriver()); action.dragAndDropBy(elements.get(0), xOffset, yOffset).perform(); } + /** + * Simulates pressing the left mouse button on the element identified by + * locator.
+ *
+ * The element is pressed without releasing the mouse button.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @see Element#mouseDownOnImage + * @see Element#mouseDownOnLink + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void mouseDown(String locator) { - info(String.format("Simulating Mouse Down on element '%s'.", locator)); + logging.info(String.format("Simulating Mouse Down on element '%s'.", locator)); List elements = elementFind(locator, true, false); if (elements.size() == 0) { - throw new Selenium2LibraryNonFatalException(String.format( - "ERROR: Element %s not found.", locator)); + throw new Selenium2LibraryNonFatalException(String.format("ERROR: Element %s not found.", locator)); } - Actions action = new Actions(webDriverCache.getCurrent()); + Actions action = new Actions(browserManagement.getCurrentWebDriver()); action.clickAndHold(elements.get(0)).perform(); } + /** + * Simulates moving the mouse away from the element identified by + * locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void mouseOut(String locator) { - info(String.format("Simulating Mouse Out on element '%s'.", locator)); + logging.info(String.format("Simulating Mouse Out on element '%s'.", locator)); List elements = elementFind(locator, true, false); if (elements.size() == 0) { - throw new Selenium2LibraryNonFatalException(String.format( - "ERROR: Element %s not found.", locator)); + throw new Selenium2LibraryNonFatalException(String.format("ERROR: Element %s not found.", locator)); } WebElement element = elements.get(0); @@ -358,63 +1066,140 @@ public void mouseOut(String locator) { int offsetX = size.getWidth() / 2 + 1; int offsetY = size.getHeight() / 2 + 1; - Actions action = new Actions(webDriverCache.getCurrent()); + Actions action = new Actions(browserManagement.getCurrentWebDriver()); action.moveToElement(element).moveByOffset(offsetX, offsetY).perform(); } + /** + * Simulates moving the mouse over the element identified by locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void mouseOver(String locator) { - info(String.format("Simulating Mouse Over on element '%s'.", locator)); + logging.info(String.format("Simulating Mouse Over on element '%s'.", locator)); List elements = elementFind(locator, true, false); if (elements.size() == 0) { - throw new Selenium2LibraryNonFatalException(String.format( - "ERROR: Element %s not found.", locator)); + throw new Selenium2LibraryNonFatalException(String.format("ERROR: Element %s not found.", locator)); } WebElement element = elements.get(0); - Actions action = new Actions(webDriverCache.getCurrent()); + Actions action = new Actions(browserManagement.getCurrentWebDriver()); action.moveToElement(element).perform(); } + /** + * Simulates releasing the left mouse button on the element identified by + * locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void mouseUp(String locator) { - info(String.format("Simulating Mouse Up on element '%s'.", locator)); + logging.info(String.format("Simulating Mouse Up on element '%s'.", locator)); List elements = elementFind(locator, true, false); if (elements.size() == 0) { - throw new Selenium2LibraryNonFatalException(String.format( - "ERROR: Element %s not found.", locator)); + throw new Selenium2LibraryNonFatalException(String.format("ERROR: Element %s not found.", locator)); } WebElement element = elements.get(0); - Actions action = new Actions(webDriverCache.getCurrent()); - action.clickAndHold(element).release(element).perform(); - } - + Actions action = new Actions(browserManagement.getCurrentWebDriver()); + action.release(element).perform(); + } + + /** + * Opens the context menu on the element identified by locator.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void openContextMenu(String locator) { List elements = elementFind(locator, true, true); - Actions action = new Actions(webDriverCache.getCurrent()); + Actions action = new Actions(browserManagement.getCurrentWebDriver()); action.contextClick(elements.get(0)).perform(); } + /** + * Simulates the given event on the element identified by + * locator.
+ *
+ * This keyword is especially useful, when the element has an OnEvent + * handler that needs to be explicitly invoked.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @param event + * The event to invoke. + */ + @RobotKeyword + @ArgumentNames({ "locator", "event" }) public void simulate(String locator, String event) { List elements = elementFind(locator, true, true); - String script = "element = arguments[0];" - + "eventName = arguments[1];" - + "if (document.createEventObject) {" - + "return element.fireEvent('on' + eventName, document.createEventObject());" - + "}" + "var evt = document.createEvent(\"HTMLEvents\");" - + "evt.initEvent(eventName, true, true);" + String script = "element = arguments[0];" + "eventName = arguments[1];" + "if (document.createEventObject) {" + + "return element.fireEvent('on' + eventName, document.createEventObject());" + "}" + + "var evt = document.createEvent(\"HTMLEvents\");" + "evt.initEvent(eventName, true, true);" + "return !element.dispatchEvent(evt);"; - ((JavascriptExecutor) webDriverCache.getCurrent()).executeScript( - script, elements.get(0), event); - } - + ((JavascriptExecutor) browserManagement.getCurrentWebDriver()).executeScript(script, elements.get(0), event); + } + + /** + * Simulates pressing key on the element identified by + * locator.
+ *
+ * Key is either a single character, or a numerical ASCII code of the key + * lead by '\\'.
+ *
+ * Key attributes for arbitrary elements are id and name. See `Introduction` + * for details about locators.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Press Keytext_fieldq# Press 'q'
Press Keylogin_button\\13# ASCII code for enter key
+ * + * @param locator + * The locator to locate the element. + * @param key + * The key to press. + */ + @RobotKeyword + @ArgumentNames({ "locator", "key" }) public void pressKey(String locator, String key) { if (key.startsWith("\\") && key.length() > 1) { - key = mapAsciiKeyCodeToKey(Integer.parseInt(key.substring(1))) - .toString(); + key = mapAsciiKeyCodeToKey(Integer.parseInt(key.substring(1))).toString(); } List element = elementFind(locator, true, true); element.get(0).sendKeys(key); @@ -424,13 +1209,32 @@ public void pressKey(String locator, String key) { // Keywords - Links // ############################## + /** + * Click on the link identified by locator.
+ *
+ * Key attributes for links are id, name, href and link text. See + * `Introduction` for details about locators.
+ * + * @param locator + * The locator to locate the link. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void clickLink(String locator) { - info(String.format("Clicking link '%s'.", locator)); + logging.info(String.format("Clicking link '%s'.", locator)); List elements = elementFind(locator, true, true, "a"); elements.get(0).click(); } + /** + * Returns a list containing ids of all links found in current page.
+ *
+ * If a link has no id, an empty string will be in the list instead.
+ * + * @return The list of link ids. + */ + @RobotKeyword public ArrayList getAllLinks() { ArrayList ret = new ArrayList(); @@ -442,45 +1246,108 @@ public ArrayList getAllLinks() { return ret; } + /** + * Simulates pressing the left mouse button on the link identified by + * locator.
+ *
+ * The element is pressed without releasing the mouse button.
+ *
+ * Key attributes for links are id, name, href and link text. See + * `Introduction` for details about locators.
+ * + * @param locator + * The locator to locate the element. + * @see Element#mouseDown + * @see Element#mouseDownOnImage + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void mouseDownOnLink(String locator) { List elements = elementFind(locator, true, true, "link"); - Actions action = new Actions(webDriverCache.getCurrent()); + Actions action = new Actions(browserManagement.getCurrentWebDriver()); action.clickAndHold(elements.get(0)).perform(); } + @RobotKeywordOverload + @ArgumentNames({ "locator" }) public void pageShouldContainLink(String locator) { - this.pageShouldContainLink(locator, "", "INFO"); + pageShouldContainLink(locator, "", "INFO"); } + @RobotKeywordOverload + @ArgumentNames({ "locator", "message=NONE" }) public void pageShouldContainLink(String locator, String message) { - this.pageShouldContainLink(locator, message, "INFO"); - } - - public void pageShouldContainLink(String locator, String message, - String logLevel) { - this.pageShouldContainElement(locator, "link", message, logLevel); - } - + pageShouldContainLink(locator, message, "INFO"); + } + + /** + * Verify the link identified by locator is found on the current + * page.
+ *
+ * Key attributes for links are id, name, href and link text. See + * `Introduction` for details about log levels and locators.
+ * + * @param locator + * The locator to locate the link. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldContainLink(String locator, String message, String logLevel) { + pageShouldContainElement(locator, "link", message, logLevel); + } + + @RobotKeywordOverload public void pageShouldNotContainLink(String locator) { - this.pageShouldNotContainLink(locator, "", "INFO"); + pageShouldNotContainLink(locator, "", "INFO"); } + @RobotKeywordOverload public void pageShouldNotContainLink(String locator, String message) { - this.pageShouldNotContainLink(locator, message, "INFO"); - } - - public void pageShouldNotContainLink(String locator, String message, - String logLevel) { - this.pageShouldNotContainElement(locator, "link", message, logLevel); + pageShouldNotContainLink(locator, message, "INFO"); + } + + /** + * Verify the link identified by locator is not found on the current + * page.
+ *
+ * Key attributes for links are id, name, href and link text. See + * `Introduction` for details about log levels and locators.
+ * + * @param locator + * The locator to locate the link. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldNotContainLink(String locator, String message, String logLevel) { + pageShouldNotContainElement(locator, "link", message, logLevel); } // ############################## // Keywords - Images // ############################## + /** + * Click on the image identified by locator.
+ *
+ * Key attributes for images are id, src and alt. See `Introduction` for + * details about locators.
+ * + * @param locator + * The locator to locate the element. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void clickImage(String locator) { - info(String.format("Clicking image '%s'.", locator)); + logging.info(String.format("Clicking image '%s'.", locator)); List elements = elementFind(locator, true, false, "image"); @@ -491,36 +1358,90 @@ public void clickImage(String locator) { element.click(); } + /** + * Simulates pressing the left mouse button on the image identified by + * locator.
+ *
+ * The element is pressed without releasing the mouse button.
+ *
+ * Key attributes for images are id, src and alt. See `Introduction` for + * details about locators.
+ * + * @param locator + * The locator to locate the element. + * @see Element#mouseDown + * @see Element#mouseDownOnLink + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void mouseDownOnImage(String locator) { List elements = elementFind(locator, true, true, "image"); - Actions action = new Actions(webDriverCache.getCurrent()); + Actions action = new Actions(browserManagement.getCurrentWebDriver()); action.clickAndHold(elements.get(0)).perform(); } + @RobotKeywordOverload + @ArgumentNames({ "locator" }) public void pageShouldContainImage(String locator) { - this.pageShouldContainImage(locator, "", "INFO"); + pageShouldContainImage(locator, "", "INFO"); } + @RobotKeywordOverload + @ArgumentNames({ "locator", "message=NONE" }) public void pageShouldContainImage(String locator, String message) { - this.pageShouldContainImage(locator, message, "INFO"); - } - - public void pageShouldContainImage(String locator, String message, - String logLevel) { + pageShouldContainImage(locator, message, "INFO"); + } + + /** + * Verify the image identified by locator is found on the current + * page.
+ *
+ * Key attributes for images are id, src and alt. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the link. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldContainImage(String locator, String message, String logLevel) { pageShouldContainElement(locator, "image", message, logLevel); } + @RobotKeywordOverload + @ArgumentNames({ "locator" }) public void pageShouldNotContainImage(String locator) { - this.pageShouldNotContainImage(locator, "", "INFO"); + pageShouldNotContainImage(locator, "", "INFO"); } + @RobotKeywordOverload + @ArgumentNames({ "locator", "message=NONE" }) public void pageShouldNotContainImage(String locator, String message) { - this.pageShouldNotContainImage(locator, message, "INFO"); - } - - public void pageShouldNotContainImage(String locator, String message, - String logLevel) { + pageShouldNotContainImage(locator, message, "INFO"); + } + + /** + * Verify the image identified by locator is not found on the current + * page.
+ *
+ * Key attributes for images are id, src and alt. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the link. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldNotContainImage(String locator, String message, String logLevel) { pageShouldNotContainElement(locator, "image", message, logLevel); } @@ -528,6 +1449,18 @@ public void pageShouldNotContainImage(String locator, String message, // Keywords - Xpath // ############################## + /** + * Returns the number of elements located the given xpath.
+ *
+ * If you wish to assert the number of located elements, use `Xpath Should + * Match X Times`.
+ * + * @param xpath + * The XPath to match page elements + * @return The number of located elements + */ + @RobotKeyword + @ArgumentNames({ "xpath" }) public int getMatchingXpathCount(String xpath) { if (!xpath.startsWith("xpath=")) { xpath = "xpath=" + xpath; @@ -537,17 +1470,34 @@ public int getMatchingXpathCount(String xpath) { return elements.size(); } + @RobotKeywordOverload + @ArgumentNames({ "xpath", "expectedXpathCount" }) public void xpathShouldMatchXTimes(String xpath, int expectedXpathCount) { - this.xpathShouldMatchXTimes(xpath, expectedXpathCount, ""); - } - - public void xpathShouldMatchXTimes(String xpath, int expectedXpathCount, - String message) { - this.xpathShouldMatchXTimes(xpath, expectedXpathCount, message, "INFO"); - } - - public void xpathShouldMatchXTimes(String xpath, int expectedXpathCount, - String message, String logLevel) { + xpathShouldMatchXTimes(xpath, expectedXpathCount, ""); + } + + @RobotKeywordOverload + @ArgumentNames({ "xpath", "expectedXpathCount", "message=NONE" }) + public void xpathShouldMatchXTimes(String xpath, int expectedXpathCount, String message) { + xpathShouldMatchXTimes(xpath, expectedXpathCount, message, "INFO"); + } + + /** + * Verify that the page contains the expectedXpathCount of elements + * located by the given xpath.
+ * + * @param xpath + * The XPath to match page elements + * @param expectedXpathCount + * The expected number of located elements + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "xpath", "expectedXpathCount", "message=NONE", "logLevel=INFO" }) + public void xpathShouldMatchXTimes(String xpath, int expectedXpathCount, String message, String logLevel) { if (!xpath.startsWith("xpath=")) { xpath = "xpath=" + xpath; } @@ -556,37 +1506,30 @@ public void xpathShouldMatchXTimes(String xpath, int expectedXpathCount, if (actualXpathCount != expectedXpathCount) { if (message == null || message.equals("")) { - message = String - .format("Xpath %s should have matched %s times but matched %s times.", - xpath, expectedXpathCount, actualXpathCount); + message = String.format("Xpath %s should have matched %s times but matched %s times.", xpath, + expectedXpathCount, actualXpathCount); } throw new Selenium2LibraryNonFatalException(message); } - log(String.format("Current page contains %s elements matching '%s'.", - actualXpathCount, xpath), logLevel); + logging.log(String.format("Current page contains %s elements matching '%s'.", actualXpathCount, xpath), + logLevel); } // ############################## // Internal Methods // ############################## - @Override - protected List elementFind(String locator, boolean firstOnly, - boolean required) { + protected List elementFind(String locator, boolean firstOnly, boolean required) { return elementFind(locator, firstOnly, required, null); } - protected List elementFind(String locator, boolean firstOnly, - boolean required, String tag) { - List elements = ElementFinder.find( - webDriverCache.getCurrent(), locator, tag); + protected List elementFind(String locator, boolean firstOnly, boolean required, String tag) { + List elements = ElementFinder.find(browserManagement.getCurrentWebDriver(), locator, tag); if (required && elements.size() == 0) { - throw new Selenium2LibraryNonFatalException( - String.format( - "Element locator '%s' did not match any elements.", - locator)); + throw new Selenium2LibraryNonFatalException(String.format( + "Element locator '%s' did not match any elements.", locator)); } if (firstOnly) { @@ -601,61 +1544,35 @@ protected List elementFind(String locator, boolean firstOnly, } protected boolean frameContains(String locator, String text) { - WebDriver current = webDriverCache.getCurrent(); + WebDriver current = browserManagement.getCurrentWebDriver(); List elements = elementFind(locator, true, true); current.switchTo().frame(elements.get(0)); - info(String.format("Searching for text from frame '%s'.", locator)); + logging.info(String.format("Searching for text from frame '%s'.", locator)); boolean found = isTextPresent(text); current.switchTo().defaultContent(); return found; } - protected String fetchText(String locator) { - List elements = elementFind(locator, true, true); - - if (elements.size() == 0) { - return null; - } - - return elements.get(0).getText(); - } - - protected String fetchValue(String locator) { - return this.fetchValue(locator, null); - } - - protected String fetchValue(String locator, String tag) { - List elements = elementFind(locator, true, false, tag); - - if (elements.size() == 0) { - return null; - } - - return elements.get(0).getAttribute("value"); - } - protected boolean isTextPresent(String text) { - String locator = String.format("xpath=//*[contains(., %s)]", - escapeXpathValue(text)); + String locator = String.format("xpath=//*[contains(., %s)]", escapeXpathValue(text)); - return this.isElementPresent(locator); + return isElementPresent(locator); } protected boolean isEnabled(String locator) { List elements = elementFind(locator, true, true); WebElement element = elements.get(0); - if (!isFormElement(element)) { - throw new Selenium2LibraryNonFatalException(String.format( - "ERROR: Element %s is not an input.", locator)); + if (!formElement.isFormElement(element)) { + throw new Selenium2LibraryNonFatalException(String.format("ERROR: Element %s is not an input.", locator)); } if (!element.isEnabled()) { return false; } String readonly = element.getAttribute("readonly"); - if (readonly.equals("readonly") || readonly.equals("true")) { + if (readonly != null && (readonly.equals("readonly") || readonly.equals("true"))) { return false; } @@ -671,19 +1588,33 @@ protected boolean isVisible(String locator) { return element.isDisplayed(); } + protected boolean isClickable(String locator) { + List webElements = elementFind(locator, true, false); + if (webElements.size() == 0) { + return false; + } + WebElement element = webElements.get(0); + return element.isDisplayed() && element.isEnabled(); + } + + protected boolean isSelected(String locator) { + List webElements = elementFind(locator, true, false); + if (webElements.size() == 0) { + return false; + } + WebElement element = webElements.get(0); + return element.isSelected(); + } + protected String[] parseAttributeLocator(String attributeLocator) { int index = attributeLocator.lastIndexOf('@'); if (index <= 0) { - throw new Selenium2LibraryNonFatalException( - String.format( - "Attribute locator '%s' does not contain an element locator.", - attributeLocator)); + throw new Selenium2LibraryNonFatalException(String.format( + "Attribute locator '%s' does not contain an element locator.", attributeLocator)); } if (index + 1 == attributeLocator.length()) { - throw new Selenium2LibraryNonFatalException( - String.format( - "Attribute locator '%s' does not contain an attribute name.", - attributeLocator)); + throw new Selenium2LibraryNonFatalException(String.format( + "Attribute locator '%s' does not contain an attribute name.", attributeLocator)); } String[] parts = new String[2]; parts[0] = attributeLocator.substring(0, index); @@ -693,7 +1624,7 @@ protected String[] parseAttributeLocator(String attributeLocator) { } protected boolean isElementPresent(String locator) { - return this.isElementPresent(locator, null); + return isElementPresent(locator, null); } protected boolean isElementPresent(String locator, String tag) { @@ -701,15 +1632,14 @@ protected boolean isElementPresent(String locator, String tag) { } protected boolean pageContains(String text) { - WebDriver current = webDriverCache.getCurrent(); + WebDriver current = browserManagement.getCurrentWebDriver(); current.switchTo().defaultContent(); if (isTextPresent(text)) { return true; } - List elements = elementFind("xpath=//frame|//iframe", - false, false); + List elements = elementFind("xpath=//frame|//iframe", false, false); Iterator it = elements.iterator(); while (it.hasNext()) { current.switchTo().frame(it.next()); @@ -723,40 +1653,6 @@ protected boolean pageContains(String text) { return false; } - protected void helperPageShouldContainElement(String locator, String tag, - String message, String logLevel) { - String name = tag != null ? tag : "element"; - if (!isElementPresent(locator, tag)) { - if (message == null || message.equals("")) { - message = String.format( - "Page should have contained %s '%s' but did not", name, - locator); - } - log(message, logLevel); - throw new Selenium2LibraryNonFatalException(message); - } else { - log(String.format("Current page contains %s '%s'.", name, locator), - logLevel); - } - } - - protected void helperPageShouldNotContainElement(String locator, - String tag, String message, String logLevel) { - String name = tag != null ? tag : "element"; - if (isElementPresent(locator, tag)) { - if (message == null || message.equals("")) { - message = String.format( - "Page should not have contained %s '%s' but did", name, - locator); - } - log(message, logLevel); - throw new Selenium2LibraryNonFatalException(message); - } else { - log(String.format("Current page does not contain %s '%s'.", name, - locator), logLevel); - } - } - protected CharSequence mapAsciiKeyCodeToKey(int keyCode) { switch (keyCode) { case 0: @@ -801,8 +1697,7 @@ protected CharSequence mapAsciiKeyCodeToKey(int keyCode) { public static String escapeXpathValue(String value) { if (value.contains("\"") && value.contains("'")) { String[] partsWoApos = value.split("'"); - return String.format("concat('%s')", - Python.join("', \"'\", '", Arrays.asList(partsWoApos))); + return String.format("concat('%s')", Python.join("', \"'\", '", Arrays.asList(partsWoApos))); } if (value.contains("'")) { return String.format("\"%s\"", value); @@ -810,9 +1705,4 @@ public static String escapeXpathValue(String value) { return String.format("'%s'", value); } - // ############################## - // Forward Declarations - // ############################## - - protected abstract boolean isFormElement(WebElement element); } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/FormElement.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/FormElement.java index d7f9c56..befdb1a 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/FormElement.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/FormElement.java @@ -4,271 +4,852 @@ import java.util.List; import org.openqa.selenium.WebElement; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywordOverload; +import org.robotframework.javalib.annotation.RobotKeywords; +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; -public abstract class FormElement extends Element { +@RobotKeywords +public class FormElement extends RunOnFailureKeywordsAdapter { + + /** + * Instantiated Element keyword bean + */ + @Autowired + protected Element element; + + /** + * Instantiated Logging keyword bean + */ + @Autowired + protected Logging logging; // ############################## // Keywords // ############################## + @RobotKeywordOverload public void submitForm() { submitForm(null); } + /** + * Submit the form identified by locator.
+ *
+ * If the locator is empty, the first form in the page will be submitted.
+ *
+ * Key attributes for forms are id and name. See `Introduction` for details + * about locators.
+ * + * @param locator + * Default=NONE. The locator to locate the form. + */ + @RobotKeyword + @ArgumentNames({ "locator=NONE" }) public void submitForm(String locator) { - info(String.format("Submitting form '%s'.", locator)); + logging.info(String.format("Submitting form '%s'.", locator)); if (locator == null) { locator = "xpath=//form"; } - List element = elementFind(locator, true, true, "form"); - element.get(0).submit(); - } - + List webElements = element.elementFind(locator, true, true, "form"); + webElements.get(0).submit(); + } + + /** + * Verify the checkbox identified by locator is selected/checked.
+ *
+ * Key attributes for checkboxes are id and name. See `Introduction` for + * details about locators.
+ * + * @param locator + * The locator to locate the checkbox. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void checkboxShouldBeSelected(String locator) { - info(String.format("Verifying checkbox '%s' is selected.", locator)); + logging.info(String.format("Verifying checkbox '%s' is selected.", locator)); WebElement element = getCheckbox(locator); if (!element.isSelected()) { - throw new Selenium2LibraryNonFatalException(String.format( - "Checkbox '%s' should have been selected.", locator)); + throw new Selenium2LibraryNonFatalException(String.format("Checkbox '%s' should have been selected.", + locator)); } } + /** + * Verify the checkbox identified by locator is not selected/checked.
+ *
+ * Key attributes for checkboxes are id and name. See `Introduction` for + * details about locators.
+ * + * @param locator + * The locator to locate the checkbox. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void checkboxShouldNotBeSelected(String locator) { - info(String.format("Verifying checkbox '%s' is selected.", locator)); + logging.info(String.format("Verifying checkbox '%s' is selected.", locator)); WebElement element = getCheckbox(locator); if (element.isSelected()) { - throw new Selenium2LibraryNonFatalException(String.format( - "Checkbox '%s' should not have been selected.", locator)); + throw new Selenium2LibraryNonFatalException(String.format("Checkbox '%s' should not have been selected.", + locator)); } } + @RobotKeywordOverload public void pageShouldContainCheckbox(String locator) { pageShouldContainCheckbox(locator, ""); } + @RobotKeywordOverload public void pageShouldContainCheckbox(String locator, String message) { pageShouldContainCheckbox(locator, message, "INFO"); } - public void pageShouldContainCheckbox(String locator, String message, - String loglevel) { - pageShouldContainElement(locator, "checkbox", message, loglevel); - } - + /** + * Verify the checkbox identified by locator is found on the current + * page.
+ *
+ * Key attributes for checkboxes are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the checkbox. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldContainCheckbox(String locator, String message, String logLevel) { + element.pageShouldContainElement(locator, "checkbox", message, logLevel); + } + + @RobotKeywordOverload public void pageShouldNotContainCheckbox(String locator) { pageShouldNotContainCheckbox(locator, ""); } + @RobotKeywordOverload public void pageShouldNotContainCheckbox(String locator, String message) { pageShouldNotContainCheckbox(locator, message, "INFO"); } - public void pageShouldNotContainCheckbox(String locator, String message, - String loglevel) { - pageShouldNotContainElement(locator, "checkbox", message, loglevel); - } - + /** + * Verify the checkbox identified by locator is not found on the + * current page.
+ *
+ * Key attributes for checkboxes are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the checkbox. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldNotContainCheckbox(String locator, String message, String logLevel) { + element.pageShouldNotContainElement(locator, "checkbox", message, logLevel); + } + + /** + * Select the checkbox identified by locator.
+ *
+ * Does nothing, if the checkbox is already selected.
+ *
+ * Key attributes for checkboxes are id and name. See `Introduction` for + * details about locators.
+ * + * @param locator + * The locator to locate the checkbox. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void selectCheckbox(String locator) { - info(String.format("Selecting checkbox '%s'.", locator)); + logging.info(String.format("Selecting checkbox '%s'.", locator)); WebElement element = getCheckbox(locator); if (!element.isSelected()) { element.click(); } } + /** + * Unselect the checkbox identified by locator.
+ *
+ * Does nothing, if the checkbox is not selected.
+ *
+ * Key attributes for checkboxes are id and name. See `Introduction` for + * details about locators.
+ * + * @param locator + * The locator to locate the checkbox. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void unselectCheckbox(String locator) { - info(String.format("Selecting checkbox '%s'.", locator)); + logging.info(String.format("Selecting checkbox '%s'.", locator)); WebElement element = getCheckbox(locator); if (element.isSelected()) { element.click(); } } + @RobotKeywordOverload public void pageShouldContainRadioButton(String locator) { pageShouldContainRadioButton(locator, ""); } + @RobotKeywordOverload public void pageShouldContainRadioButton(String locator, String message) { pageShouldContainRadioButton(locator, message, "INFO"); } - public void pageShouldContainRadioButton(String locator, String message, - String loglevel) { - pageShouldContainElement(locator, "radio button", message, loglevel); - } - + /** + * Verify the radio button identified by locator is found on the + * current page.
+ *
+ * Key attributes for radio buttons are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the radio button. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldContainRadioButton(String locator, String message, String logLevel) { + element.pageShouldContainElement(locator, "radio button", message, logLevel); + } + + @RobotKeywordOverload public void pageShouldNotContainRadioButton(String locator) { pageShouldNotContainRadioButton(locator, ""); } + @RobotKeywordOverload public void pageShouldNotContainRadioButton(String locator, String message) { pageShouldNotContainRadioButton(locator, message, "INFO"); } - public void pageShouldNotContainRadioButton(String locator, String message, - String loglevel) { - pageShouldNotContainElement(locator, "radio button", message, loglevel); - } - + /** + * Verify the radio button identified by locator is not found on the + * current page.
+ *
+ * Key attributes for radio buttons are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the radio button. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldNotContainRadioButton(String locator, String message, String logLevel) { + element.pageShouldNotContainElement(locator, "radio button", message, logLevel); + } + + /** + * Verify the radio button group identified by groupName has its + * selection set to value.
+ *
+ * See `Select Radio Button` for details about locating radio buttons.
+ * + * @param groupName + * The radio button group name. + * @param value + * The expected value. + */ + @RobotKeyword + @ArgumentNames({ "groupName", "value" }) public void radioButtonShouldBeSetTo(String groupName, String value) { - info(String.format("Verifying radio button '%s' has selection '%s'.", - groupName, value)); + logging.info(String.format("Verifying radio button '%s' has selection '%s'.", groupName, value)); List elements = getRadioButtons(groupName); String actualValue = getValueFromRadioButtons(elements); if (actualValue == null || !actualValue.equals(value)) { - throw new Selenium2LibraryNonFatalException( - String.format( - "Selection of radio button '%s' should have been '%s' but was '%s'", - groupName, value, actualValue)); + throw new Selenium2LibraryNonFatalException(String.format( + "Selection of radio button '%s' should have been '%s' but was '%s'", groupName, value, actualValue)); } } + /** + * Verify the radio button group identified by groupName has no + * selection.
+ *
+ * See `Select Radio Button` for details about locating radio buttons.
+ * + * @param groupName + * The radio button group name. + */ + @RobotKeyword + @ArgumentNames({ "groupName" }) public void radioButtonShouldNotBeSelected(String groupName) { - info(String.format("Verifying radio button '%s' has no selection.", - groupName)); + logging.info(String.format("Verifying radio button '%s' has no selection.", groupName)); List elements = getRadioButtons(groupName); String actualValue = getValueFromRadioButtons(elements); if (actualValue != null) { - throw new Selenium2LibraryNonFatalException( - String.format( - "Radio button group '%s' should not have had selection, but '%s' was selected", - groupName, actualValue)); + throw new Selenium2LibraryNonFatalException(String.format( + "Radio button group '%s' should not have had selection, but '%s' was selected", groupName, + actualValue)); } } + /** + * Sets the selection of the radio button group identified by + * groupName to value.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Select Radio ButtonsizeXL# Matches HTML like <input type="radio" name="size" + * value="XL">XL</input>
Select Radio ButtonsizesizeXL# Matches HTML like <input type="radio" name="size" value="XL" + * id="sizeXL">XL</input>
+ * + * @param groupName + * The radio button group name. + * @param value + * The value or id attribute of the radio button to set. + */ + @RobotKeyword + @ArgumentNames({ "groupName", "value" }) public void selectRadioButton(String groupName, String value) { - info(String.format("Selecting '%s' from radio button '%s'.", value, - groupName)); + logging.info(String.format("Selecting '%s' from radio button '%s'.", value, groupName)); WebElement element = getRadioButtonWithValue(groupName, value); if (!element.isSelected()) { element.click(); } } + /** + * Types the given filePath into the input field identified by + * locator.
+ *
+ * This keyword is most often used to input files into upload forms. The + * file specified with filePath must be available on the same host where the + * Selenium Server is running.
+ *
+ * Example: + * + * + * + * + * + * + *
Choose Filemy_upload_field/home/user/files/trades.csv
+ * Key attributes for input fields are id and name. See `Introduction` for + * details about locators.
+ * + * @param locator + * The locator to locate the input field. + * @param filePath + * The file path to input + */ + @RobotKeyword + @ArgumentNames({ "locator", "filePath" }) public void chooseFile(String locator, String filePath) { if (!new File(filePath).isFile()) { - info(String.format( - "File '%s' does not exist on the local file system", - filePath)); + logging.info(String.format("File '%s' does not exist on the local file system", filePath)); } - elementFind(locator, true, true).get(0).sendKeys(filePath); - } - + element.elementFind(locator, true, true).get(0).sendKeys(filePath); + } + + /** + * Types the given text into the password field identified by + * locator.
+ *
+ * Key attributes for input fields are id and name. See `Introduction` for + * details about locators.
+ * + * @param locator + * The locator to locate the password field. + * @param text + * The password to input + */ + @RobotKeyword + @ArgumentNames({ "locator", "text" }) public void inputPassword(String locator, String text) { - info(String.format("Typing password into text field '%s'", locator)); + logging.info(String.format("Typing password into text field '%s'", locator)); inputTextIntoTextField(locator, text); } + /** + * Types the given text into the text field identified by + * locator.
+ *
+ * Key attributes for input fields are id and name. See `Introduction` for + * details about locators.
+ * + * @param locator + * The locator to locate the text field. + * @param text + * The password to input + */ + @RobotKeyword + @ArgumentNames({ "locator", "text" }) public void inputText(String locator, String text) { - info(String.format("Typing text '%s' into text field '%s'", text, - locator)); + logging.info(String.format("Typing text '%s' into text field '%s'", text, locator)); inputTextIntoTextField(locator, text); } + @RobotKeywordOverload public void pageShouldContainTextfield(String locator) { pageShouldContainTextfield(locator, ""); } + @RobotKeywordOverload public void pageShouldContainTextfield(String locator, String message) { pageShouldContainTextfield(locator, message, "INFO"); } - public void pageShouldContainTextfield(String locator, String message, - String loglevel) { - pageShouldContainElement(locator, "text field", message, loglevel); - } - + /** + * Verify the text field identified by locator is found on the + * current page.
+ *
+ * Key attributes for text field are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the text field. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldContainTextfield(String locator, String message, String logLevel) { + element.pageShouldContainElement(locator, "text field", message, logLevel); + } + + @RobotKeywordOverload public void pageShouldNotContainTextfield(String locator) { pageShouldNotContainTextfield(locator, ""); } + @RobotKeywordOverload public void pageShouldNotContainTextfield(String locator, String message) { pageShouldNotContainTextfield(locator, message, "INFO"); } - public void pageShouldNotContainTextfield(String locator, String message, - String loglevel) { - pageShouldNotContainElement(locator, "text field", message, loglevel); - } - - public void textfieldShouldContain(String locator, String expected) { - textfieldShouldContain(locator, expected, ""); - } - - public void textfieldShouldContain(String locator, String expected, - String message) { - String actual = getValue(locator, "text field"); - if (!actual.contains(expected)) { + /** + * Verify the text field identified by locator is not found on the + * current page.
+ *
+ * Key attributes for text field are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the text field. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldNotContainTextfield(String locator, String message, String logLevel) { + element.pageShouldNotContainElement(locator, "text field", message, logLevel); + } + + @RobotKeywordOverload + public void textfieldValueShouldBe(String locator, String text) { + textfieldValueShouldBe(locator, text, ""); + } + + /** + * Verify the text field identified by locator is exactly + * text.
+ *
+ * Key attributes for text field are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the text field. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + * + * @see FormElement#textfieldShouldContain + * @see FormElement#textfieldShouldNotContain + * @see FormElement#textfieldValueShouldNotBe + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void textfieldValueShouldBe(String locator, String text, String message) { + String actual = element.getValue(locator, "text field"); + if (!actual.contains(text)) { if (message == null) { - message = String - .format("Text field '%s' should have contained text '%s' but it contained '%s'", - locator, expected, actual); + message = String.format("Value of text field '%s' should have been '%s' but was '%s'", locator, text, + actual); } throw new Selenium2LibraryNonFatalException(message); } - info(String.format("Text field '%s' contains text '%s'.", locator, - expected)); - } - - public void textfieldValueShouldBe(String locator, String expected) { - textfieldValueShouldBe(locator, expected, ""); - } - - public void textfieldValueShouldBe(String locator, String expected, - String message) { - String actual = getValue(locator, "text field"); - if (!actual.contains(expected)) { + logging.info(String.format("Content of text field '%s' is '%s'.", locator, text)); + } + + @RobotKeywordOverload + public void textfieldValueShouldNotBe(String locator, String text) { + textfieldValueShouldNotBe(locator, text, ""); + } + + /** + * Verify the text field identified by locator is not exactly + * text.
+ *
+ * Key attributes for text field are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the text field. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + * + * @see FormElement#textfieldShouldContain + * @see FormElement#textfieldShouldNotContain + * @see FormElement#textfieldValueShouldBe + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void textfieldValueShouldNotBe(String locator, String text, String message) { + String actual = element.getValue(locator, "text field"); + if (actual.contains(text)) { if (message == null) { - message = String - .format("Value of text field '%s' should have been '%s' but was '%s'", - locator, expected, actual); + message = String.format("Value of text field '%s' should not have been '%s' but was '%s'", locator, + text, actual); } throw new Selenium2LibraryNonFatalException(message); } - info(String.format("Content of text field '%s' is '%s'.", locator, - expected)); - } - + logging.info(String.format("Content of text field '%s' is '%s'.", locator, text)); + } + + @RobotKeywordOverload + public void textfieldShouldContain(String locator, String text) { + textfieldShouldContain(locator, text, ""); + } + + /** + * Verify the text field identified by locator contains text.
+ *
+ * Key attributes for text field are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the text field. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + * + * @see FormElement#textfieldShouldNotContain + * @see FormElement#textfieldValueShouldBe + * @see FormElement#textfieldValueShouldNotBe + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void textfieldShouldContain(String locator, String text, String message) { + String actual = element.getValue(locator, "text field"); + if (!actual.contains(text)) { + if (message == null) { + message = String.format("Text field '%s' should have contained text '%s', but was '%s'", locator, text, + actual); + } + throw new Selenium2LibraryNonFatalException(message); + } + logging.info(String.format("Text field '%s' contains text '%s'.", locator, text)); + } + + @RobotKeywordOverload + public void textfieldShouldNotContain(String locator, String text) { + textfieldShouldNotContain(locator, text, ""); + } + + /** + * Verify the text field identified by locator does not contain + * text.
+ *
+ * Key attributes for text field are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the text field. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + * + * @see FormElement#textfieldShouldContain + * @see FormElement#textfieldValueShouldBe + * @see FormElement#textfieldValueShouldNotBe + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void textfieldShouldNotContain(String locator, String text, String message) { + String actual = element.getValue(locator, "text field"); + if (actual.contains(text)) { + if (message == null) { + message = String.format("Text field '%s' should not have contained text '%s', but was '%s'", locator, + text, actual); + } + throw new Selenium2LibraryNonFatalException(message); + } + logging.info(String.format("Text field '%s' contains text '%s'.", locator, text)); + } + + @RobotKeywordOverload + public void textareaShouldContain(String locator, String text) { + textareaShouldContain(locator, text, ""); + } + + /** + * Verify the text area identified by locator contains text.
+ *
+ * Key attributes for text areas are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the text area. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + * + * @see FormElement#textareaShouldNotContain + * @see FormElement#textareaValueShouldBe + * @see FormElement#textareaValueShouldNotBe + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void textareaShouldContain(String locator, String text, String message) { + String actual = element.getValue(locator, "text area"); + if (!actual.contains(text)) { + if (message == null) { + message = String.format("Text area '%s' should have contained text '%s', but was '%s'", locator, text, + actual); + } + throw new Selenium2LibraryNonFatalException(message); + } + logging.info(String.format("Text field '%s' contains text '%s'.", locator, text)); + } + + @RobotKeywordOverload + public void textareaShouldNotContain(String locator, String text) { + textareaShouldNotContain(locator, text, ""); + } + + /** + * Verify the text area identified by locator does not contain text.
+ *
+ * Key attributes for text areas are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the text area. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + * + * @see FormElement#textareaShouldContain + * @see FormElement#textareaValueShouldBe + * @see FormElement#textareaValueShouldNotBe + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void textareaShouldNotContain(String locator, String text, String message) { + String actual = element.getValue(locator, "text area"); + if (!actual.contains(text)) { + if (message == null) { + message = String.format("Text area '%s' should not have contained text '%s', but was '%s'", locator, text, + actual); + } + throw new Selenium2LibraryNonFatalException(message); + } + logging.info(String.format("Text field '%s' contains text '%s'.", locator, text)); + } + + @RobotKeywordOverload + public void textareaValueShouldBe(String locator, String text) { + textareaValueShouldBe(locator, text, ""); + } + + /** + * Verify the text area identified by locator is exactly + * text.
+ *
+ * Key attributes for text area are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the text area. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + * + * @see FormElement#textareaShouldContain + * @see FormElement#textareaShouldNotContain + * @see FormElement#textareaValueShouldNotBe + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void textareaValueShouldBe(String locator, String text, String message) { + String actual = element.getValue(locator, "text area"); + if (!actual.contains(text)) { + if (message == null) { + message = String.format("Value of text area '%s' should have been '%s' but was '%s'", locator, text, + actual); + } + throw new Selenium2LibraryNonFatalException(message); + } + logging.info(String.format("Content of text area '%s' is '%s'.", locator, text)); + } + + @RobotKeywordOverload + public void textareaValueShouldNotBe(String locator, String text) { + textareaValueShouldNotBe(locator, text, ""); + } + + /** + * Verify the text area identified by locator is not exactly + * text.
+ *
+ * Key attributes for text area are id and name. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the text area. + * @param text + * The text to verify. + * @param message + * Default=NONE. Optional custom error message. + * + * @see FormElement#textareaShouldContain + * @see FormElement#textareaShouldNotContain + * @see FormElement#textareaValueShouldBe + */ + @RobotKeyword + @ArgumentNames({ "locator", "text", "message=NONE" }) + public void textareaValueShouldNotBe(String locator, String text, String message) { + String actual = element.getValue(locator, "text area"); + if (actual.contains(text)) { + if (message == null) { + message = String.format("Value of text area '%s' should not have been '%s' but was '%s'", locator, + text, actual); + } + throw new Selenium2LibraryNonFatalException(message); + } + logging.info(String.format("Content of text area '%s' is '%s'.", locator, text)); + } + + /** + * Click on the button identified by locator.
+ *
+ * Key attributes for buttons are id, name and value. See `Introduction` for + * details about locators.
+ * + * @param locator + * The locator to locate the link. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void clickButton(String locator) { - info(String.format("Clicking button '%s'.", locator)); - List elements = elementFind(locator, true, false, "input"); + logging.info(String.format("Clicking button '%s'.", locator)); + List elements = element.elementFind(locator, true, false, "input"); if (elements.size() == 0) { - elements = elementFind(locator, true, true, "button"); + elements = element.elementFind(locator, true, true, "button"); } elements.get(0).click(); } + @RobotKeywordOverload public void pageShouldContainButton(String locator) { pageShouldContainButton(locator, ""); } + @RobotKeywordOverload public void pageShouldContainButton(String locator, String message) { pageShouldContainButton(locator, message, "INFO"); } - public void pageShouldContainButton(String locator, String message, - String loglevel) { + /** + * Verify the button identified by locator is found on the current + * page.
+ *
+ * Key attributes for buttons are id, name and value. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the button. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldContainButton(String locator, String message, String logLevel) { try { - pageShouldContainElement(locator, "input", message, loglevel); - } catch (AssertionError ae) { - pageShouldContainElement(locator, "button", message, loglevel); + element.pageShouldContainElement(locator, "input", message, logLevel); + } catch (Selenium2LibraryNonFatalException e) { + element.pageShouldContainElement(locator, "button", message, logLevel); } } + @RobotKeywordOverload public void pageShouldNotContainButton(String locator) { pageShouldNotContainButton(locator, ""); } + @RobotKeywordOverload public void pageShouldNotContainButton(String locator, String message) { pageShouldNotContainButton(locator, message, "INFO"); } - public void pageShouldNotContainButton(String locator, String message, - String loglevel) { - pageShouldNotContainElement(locator, "input", message, loglevel); - pageShouldNotContainElement(locator, "button", message, loglevel); + /** + * Verify the button identified by locator is not found on the + * current page.
+ *
+ * Key attributes for buttons are id, name and value. See `Introduction` for + * details about log levels and locators.
+ * + * @param locator + * The locator to locate the button. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldNotContainButton(String locator, String message, String logLevel) { + element.pageShouldNotContainElement(locator, "input", message, logLevel); + element.pageShouldNotContainElement(locator, "button", message, logLevel); } // ############################## @@ -276,22 +857,20 @@ public void pageShouldNotContainButton(String locator, String message, // ############################## protected WebElement getCheckbox(String locator) { - return elementFind(locator, true, true, "input").get(0); + return element.elementFind(locator, true, true, "input").get(0); } protected List getRadioButtons(String groupName) { - String xpath = String.format( - "xpath=//input[@type='radio' and @name='%s']", groupName); - debug("Radio group locator: " + xpath); - return elementFind(xpath, false, true); + String xpath = String.format("xpath=//input[@type='radio' and @name='%s']", groupName); + logging.debug("Radio group locator: " + xpath); + return element.elementFind(xpath, false, true); } protected WebElement getRadioButtonWithValue(String groupName, String value) { - String xpath = String - .format("xpath=//input[@type='radio' and @name='%s' and (@value='%s' or @id='%s')]", - groupName, value, value); - debug("Radio group locator: " + xpath); - return elementFind(xpath, true, true).get(0); + String xpath = String.format("xpath=//input[@type='radio' and @name='%s' and (@value='%s' or @id='%s')]", + groupName, value, value); + logging.debug("Radio group locator: " + xpath); + return element.elementFind(xpath, true, true).get(0); } protected String getValueFromRadioButtons(List elements) { @@ -304,19 +883,17 @@ protected String getValueFromRadioButtons(List elements) { } protected void inputTextIntoTextField(String locator, String text) { - WebElement element = elementFind(locator, true, true).get(0); - element.clear(); - element.sendKeys(text); + WebElement webElement = element.elementFind(locator, true, true).get(0); + webElement.clear(); + webElement.sendKeys(text); } - @Override protected boolean isFormElement(WebElement element) { if (element == null) { return false; } String tag = element.getTagName().toLowerCase(); - return tag == "input" || tag == "select" || tag == "textarea" - || tag == "button" || tag == "option"; + return "input".equals(tag) || "select".equals(tag) || "textarea".equals(tag) || "button".equals(tag) || "option".equals(tag); } } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/JavaScript.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/JavaScript.java index c9fb079..f4b4fae 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/JavaScript.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/JavaScript.java @@ -11,93 +11,299 @@ import org.openqa.selenium.Alert; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriverException; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywordOverload; +import org.robotframework.javalib.annotation.RobotKeywords; +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; import com.github.markusbernhardt.selenium2library.utils.Python; -public abstract class JavaScript extends FormElement { +@RobotKeywords +public class JavaScript extends RunOnFailureKeywordsAdapter { + + protected boolean acceptOnNextConfirmationDefault = true; + protected boolean acceptOnNextConfirmation = true; + + /** + * Instantiated BrowserManagement keyword bean + */ + @Autowired + protected BrowserManagement browserManagement; + + /** + * Instantiated Logging keyword bean + */ + @Autowired + protected Logging logging; // ############################## // Keywords // ############################## + @RobotKeywordOverload public void alertShouldBePresent() { alertShouldBePresent(""); } + /** + * Verify an alert is present and dismiss it.
+ *
+ * If text is a non-empty string, then it is also verified that the + * message of the alert equals to text.
+ *
+ * Will fail if no alert is present. Note that following keywords will fail + * unless the alert is confirmed by this keyword or another like `Confirm + * Action`.
+ * + * @param text + * Default=NONE. The alert message to verify. + */ + @RobotKeyword + @ArgumentNames({ "text=NONE" }) public void alertShouldBePresent(String text) { - String alertText = getAlertMessage(); + String alertText = confirmAction(); if (text != null && !alertText.equals(text)) { - throw new Selenium2LibraryNonFatalException(String.format( - "Alert text should have been '%s' but was '%s'", text, - alertText)); + throw new Selenium2LibraryNonFatalException(String.format("Alert text should have been '%s' but was '%s'", + text, alertText)); } } + /** + * Cancel will be selected the next time a confirmation dialog appears.
+ *
+ * Note that every time a confirmation comes up, it must be confirmed by the + * keywords 'Alert Should Be Present' or `Confirm Action`. Otherwise all + * following operations will fail.
+ */ + @RobotKeyword public void chooseCancelOnNextConfirmation() { - cancelOnNextConfirmation = true; + acceptOnNextConfirmation = false; } + /** + * Ok will be selected the next time a confirmation dialog appears.
+ *
+ * Note that every time a confirmation comes up, it must be confirmed by the + * keywords 'Alert Should Be Present' or `Confirm Action`. Otherwise all + * following operations will fail.
+ */ + @RobotKeyword public void chooseOkOnNextConfirmation() { - cancelOnNextConfirmation = false; + acceptOnNextConfirmation = true; + } + + /** + * Cancel will as default be selected from now on every time a confirmation + * dialog appears.
+ *
+ * Note that every time a confirmation comes up, it must be confirmed by the + * keywords 'Alert Should Be Present' or `Confirm Action`. Otherwise all + * following operations will fail.
+ */ + @RobotKeyword + public void chooseCancelOnConfirmation() { + acceptOnNextConfirmationDefault = false; + acceptOnNextConfirmation = false; } + /** + * Ok will as default be selected from now on every time a confirmation + * dialog appears.
+ *
+ * Note that every time a confirmation comes up, it must be confirmed by the + * keywords 'Alert Should Be Present' or `Confirm Action`. Otherwise all + * following operations will fail.
+ */ + @RobotKeyword + public void chooseOkOnConfirmation() { + acceptOnNextConfirmationDefault = true; + acceptOnNextConfirmation = true; + } + + /** + * Dismisses currently shown confirmation dialog and returns its message.
+ *
+ * By default, this keyword chooses 'OK' option from the dialog. If 'Cancel' + * needs to be chosen, keyword `Choose Cancel On Next Confirmation` must be + * called before the action that causes the confirmation dialog to be shown.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Click ButtonSend# Shows a confirmation dialog
${message}=Confirm Action# Chooses Ok
Should Be Equal${message}Are your sure?# Check dialog message
Choose Cancel On Next Confirmation# Choose cancel on next `Confirm Action`
Click ButtonSend# Shows a confirmation dialog
Confirm Action# Chooses Cancel
+ * + * @return The dialog message. + */ + @RobotKeyword public String confirmAction() { - String text = closeAlert(!cancelOnNextConfirmation); - cancelOnNextConfirmation = false; - return text; + try { + Alert alert = browserManagement.getCurrentWebDriver().switchTo().alert(); + String text = alert.getText().replace("\n", ""); + if (acceptOnNextConfirmation) { + alert.accept(); + } else { + alert.dismiss(); + } + acceptOnNextConfirmation = acceptOnNextConfirmationDefault; + return text; + } catch (WebDriverException wde) { + throw new Selenium2LibraryNonFatalException("There were no alerts"); + } } + /** + * Execute the given JavaScript code.
+ *
+ * The given code may contain multiple lines of code, but must contain a + * return statement (with the value to be returned) at the end.
+ *
+ * The given code may be divided into multiple cells in the test data. In + * that case, the parts are concatenated together without adding spaces. If + * the given code is an absolute path to an existing file, the JavaScript to + * execute will be read from that file. Forward slashes work as a path + * separator on all operating systems.
+ *
+ * Note that by default the code will be executed in the context of the + * Selenium object itself, so this will refer to the Selenium object. + * Use window to refer to the window of your application, e.g. + * window.document.getElementById('foo').
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + *
Execute JavaScriptreturn window.my_js_function('arg1', 'arg2');# Directly execute the JavaScript
Execute JavaScript${CURDIR}/js_to_execute.js# Load the JavaScript to execute from file
+ * + * @param code + * The JavaScript code or a file name. + * @return The return value of the executed code. + */ + @RobotKeyword + @ArgumentNames({ "*code" }) public Object executeJavascript(String... code) { String js = getJavascriptToExecute(Python.join("", Arrays.asList(code))); String.format("Executing JavaScript:\n%s", js); - return ((JavascriptExecutor) webDriverCache.getCurrent()) - .executeScript(js); + return ((JavascriptExecutor) browserManagement.getCurrentWebDriver()).executeScript(js); } + /** + * Execute the given JavaScript code asynchronously.
+ *
+ * The given code may contain multiple lines of code, but must contain a + * return statement (with the value to be returned) at the end.
+ *
+ * The given code may be divided into multiple cells in the test data. In + * that case, the parts are concatenated together without adding spaces. If + * the given code is an absolute path to an existing file, the JavaScript to + * execute will be read from that file. Forward slashes work as a path + * separator on all operating systems.
+ *
+ * Note that by default the code will be executed in the context of the + * Selenium object itself, so this will refer to the Selenium object. + * Use window to refer to the window of your application, e.g. + * window.document.getElementById('foo').
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + *
Execute Async JavaScriptreturn window.my_js_function('arg1', 'arg2');# Directly execute the JavaScript
Execute Async JavaScript${CURDIR}/js_to_execute.js# Load the JavaScript to execute from file
+ * + * @param code + * The JavaScript code or a file name. + * @return The return value of the executed code. + */ + @RobotKeyword + @ArgumentNames({ "*code" }) public Object executeAsyncJavascript(String... code) { String js = getJavascriptToExecute(Python.join("", Arrays.asList(code))); String.format("Executing JavaScript:\n%s", js); - return ((JavascriptExecutor) webDriverCache.getCurrent()) - .executeAsyncScript(js); + return ((JavascriptExecutor) browserManagement.getCurrentWebDriver()).executeAsyncScript(js); } + /** + * Returns the text of current JavaScript alert.
+ *
+ * This keyword will fail if no alert is present. Note that following + * keywords will fail unless the alert is confirmed by the keywords 'Alert + * Should Be Present' or `Confirm Action`. + * + * @return The alert message. + */ + @RobotKeyword public String getAlertMessage() { - return closeAlert(); - } - - // ############################## - // Internal Methods - // ############################## - - protected boolean cancelOnNextConfirmation = false; - - protected String closeAlert() { - return closeAlert(false); - } - - protected String closeAlert(boolean confirm) { - Alert alert = null; try { - alert = webDriverCache.getCurrent().switchTo().alert(); + Alert alert = browserManagement.getCurrentWebDriver().switchTo().alert(); String text = alert.getText().replace("\n", ""); - if (!confirm) { - alert.dismiss(); - } else { - alert.accept(); - } return text; } catch (WebDriverException wde) { throw new Selenium2LibraryNonFatalException("There were no alerts"); } } + // ############################## + // Internal Methods + // ############################## + protected static String readFile(String path) throws IOException { FileInputStream stream = new FileInputStream(new File(path)); try { FileChannel fc = stream.getChannel(); - MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, - fc.size()); + MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); /* Instead of using default, pass in a decoder. */ return Charset.defaultCharset().decode(bb).toString(); } finally { @@ -110,14 +316,12 @@ protected String getJavascriptToExecute(String code) { if (!new File(codepath).isFile()) { return code; } - html(String.format( - "Reading JavaScript from file %s.", + logging.html(String.format("Reading JavaScript from file %s.", codepath.replace(File.separatorChar, '/'), codepath)); try { return readFile(codepath); } catch (IOException e) { - throw new Selenium2LibraryNonFatalException( - "Cannot read JavaScript file: " + codepath); + throw new Selenium2LibraryNonFatalException("Cannot read JavaScript file: " + codepath); } } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Logging.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Logging.java index 5cbf800..399b1d3 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Logging.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Logging.java @@ -3,20 +3,26 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.python.core.PyString; import org.python.util.PythonInterpreter; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywordOverload; +import org.robotframework.javalib.annotation.RobotKeywords; +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; -import com.github.markusbernhardt.selenium2library.utils.Python; -public abstract class Logging extends JavaScript { +@RobotKeywords +public class Logging extends RunOnFailureKeywordsAdapter { protected final static Map VALID_LOG_LEVELS; + protected static String logDir = null; static { VALID_LOG_LEVELS = new HashMap(); @@ -27,44 +33,304 @@ public abstract class Logging extends JavaScript { VALID_LOG_LEVELS.put("warn", new String[] { "warn", "" }); } + /** + * Instantiated BrowserManagement keyword bean + */ + @Autowired + protected BrowserManagement browserManagement; + + // ############################## + // Keywords + // ############################## + + @RobotKeywordOverload + public List logWindowIdentifiers() { + return logWindowIdentifiers("INFO"); + } + + /** + * Logs and returns the id attributes of all windows known to the current + * browser instance.
+ * + * @param logLevel + * Default=INFO. Optional log level. + * @return List of window id attributes. + * + * @see BrowserManagement#getWindowIdentifiers + */ + @RobotKeyword + @ArgumentNames({ "logLevel=INFO" }) + public List logWindowIdentifiers(String logLevel) { + List windowIdentifiers = browserManagement.getWindowIdentifiers(); + for (String windowIdentifier : windowIdentifiers) { + log(windowIdentifier, logLevel); + } + return windowIdentifiers; + } + + @RobotKeywordOverload + public List logWindowNames() { + return logWindowNames("INFO"); + } + + /** + * Logs and returns the names of all windows known to the current browser + * instance.
+ *
+ * See `Introduction` for details about the logLevel.
+ * + * @param logLevel + * Default=INFO. Optional log level. + * @return List of windows names. + * + * @see BrowserManagement#getWindowNames + */ + @RobotKeyword + @ArgumentNames({ "logLevel=INFO" }) + public List logWindowNames(String logLevel) { + List windowIdentifiers = browserManagement.getWindowNames(); + for (String windowIdentifier : windowIdentifiers) { + log(windowIdentifier, logLevel); + } + return windowIdentifiers; + } + + @RobotKeywordOverload + public List logWindowTitles() { + return logWindowTitles("INFO"); + } + + /** + * Logs and returns the titles of all windows known to the current browser + * instance.
+ *
+ * See `Introduction` for details about the logLevel.
+ * + * @param logLevel + * Default=INFO. Optional log level. + * @return List of window titles. + * + * @see BrowserManagement#getWindowTitles + */ + @RobotKeyword + @ArgumentNames({ "logLevel=INFO" }) + public List logWindowTitles(String logLevel) { + List windowIdentifiers = browserManagement.getWindowTitles(); + for (String windowIdentifier : windowIdentifiers) { + log(windowIdentifier, logLevel); + } + return windowIdentifiers; + } + + @RobotKeywordOverload + public String logLocation() { + return logLocation("INFO"); + } + + /** + * Logs and returns the location of the current browser instance.
+ *
+ * See `Introduction` for details about the logLevel.
+ * + * @param logLevel + * Default=INFO. Optional log level. + * @return The current location. + * + * @see BrowserManagement#getLocation + */ + @RobotKeyword + @ArgumentNames({ "logLevel=INFO" }) + public String logLocation(String logLevel) { + String actual = browserManagement.getLocation(); + log(actual, logLevel); + return actual; + } + + @RobotKeywordOverload + public String logSource() { + return logSource("INFO"); + } + + /** + * Logs and returns the entire html source of the current page or frame.
+ *
+ * See `Introduction` for details about the logLevel.
+ * + * @param logLevel + * Default=INFO. Optional log level. + * @return The entire html source. + * + * @see BrowserManagement#getSource + */ + @RobotKeyword + @ArgumentNames({ "logLevel=INFO" }) + public String logSource(String logLevel) { + String actual = browserManagement.getSource(); + log(actual, logLevel); + return actual; + } + + @RobotKeywordOverload + public String logTitle() { + return logTitle("INFO"); + } + + /** + * Logs and returns the title of current page.
+ *
+ * See `Introduction` for details about the logLevel.
+ * + * @param logLevel + * Default=INFO. Optional log level. + * @return The page title. + * + * @see BrowserManagement#getSource + */ + @RobotKeyword + @ArgumentNames({ "logLevel=INFO" }) + public String logTitle(String logLevel) { + String actual = browserManagement.getTitle(); + log(actual, logLevel); + return actual; + } + + @RobotKeywordOverload + public String logSystemInfo() { + return logSystemInfo("INFO"); + } + + /** + * Logs and returns basic system information about the execution + * environment.
+ *
+ * See `Introduction` for details about the logLevel.
+ * + * @param logLevel + * Default=INFO. Optional log level. + * @return System information. + * + * @see BrowserManagement#getSystemInfo + */ + @RobotKeyword + @ArgumentNames({ "logLevel=INFO" }) + public String logSystemInfo(String logLevel) { + String actual = browserManagement.getSystemInfo(); + log(actual, logLevel); + return actual; + } + + @RobotKeywordOverload + public String logRemoteCapabilities() { + return logRemoteCapabilities("INFO"); + } + + /** + * Logs and returns the actually supported capabilities of the remote + * browser instance.
+ *
+ * Not all server implementations will support every WebDriver feature. + * Therefore, the client and server should use JSON objects with the + * properties listed below when describing which features a user requests + * that a session support. If a session cannot support a capability that + * is requested in the desired capabilities, no error is thrown; a + * read-only capabilities object is returned that indicates the capabilities + * the session actually supports. For more information see: DesiredCapabilities
+ *
+ * See `Introduction` for details about the logLevel.
+ * + * @param logLevel + * Default=INFO. Optional log level. + * @return The capabilities of the remote node. + * + * @see BrowserManagement#getRemoteCapabilities + */ + @RobotKeyword + @ArgumentNames({ "logLevel=INFO" }) + public String logRemoteCapabilities(String logLevel) { + String actual = browserManagement.getRemoteCapabilities(); + log(actual, logLevel); + return actual; + } + + @RobotKeywordOverload + public String logRemoteSessionId() { + return logRemoteSessionId("INFO"); + } + + /** + * Logs and returns the session id of the remote browser instance.
+ *
+ * See `Introduction` for details about the logLevel.
+ * + * @param logLevel + * Default=INFO. Optional log level. + * @return The remote session id. + * + * @see BrowserManagement#getRemoteSessionId + */ + @RobotKeyword + @ArgumentNames({ "logLevel=INFO" }) + public String logRemoteSessionId(String logLevel) { + String actual = browserManagement.getRemoteSessionId(); + log(actual, logLevel); + return actual; + } + + /** + * Set the logDirectory, where captured screenshots are stored, to + * some custom path.
+ *
+ * Fails, if either the given path does not exist, is no directory or is not + * writable.
+ * + * @param logDirectory + * The directory to log to. + */ + @RobotKeyword + @ArgumentNames({ "logDirectory" }) + public void setLogDirectory(String logDirectory) throws Exception { + File file = new File(logDirectory); + + if (file.exists() && file.isDirectory() && file.canWrite()) { + Logging.setLogDir(file.getAbsolutePath()); + } else { + throw new Exception("Location given as parameter: " + logDirectory + + " must exist and must be a writeable directory!"); + } + } + // ############################## // Internal Methods // ############################## - @Override protected void trace(String msg) { log(msg, "trace"); } - @Override protected void debug(String msg) { log(msg, "debug"); } - @Override protected void info(String msg) { log(msg, "info"); } - @Override protected void html(String msg) { log(msg, "html"); } - @Override protected void warn(String msg) { log(msg, "warn"); } - @Override protected void log(String msg, String logLevel) { - String[] methodParameters = VALID_LOG_LEVELS - .get(logLevel.toLowerCase()); + String[] methodParameters = VALID_LOG_LEVELS.get(logLevel.toLowerCase()); if (methodParameters != null) { log0(msg, methodParameters[0], methodParameters[1]); } else { - throw new Selenium2LibraryNonFatalException(String.format( - "Given log level %s is invalid.", logLevel)); + throw new Selenium2LibraryNonFatalException(String.format("Given log level %s is invalid.", logLevel)); } } @@ -74,8 +340,7 @@ protected void log0(String msg, String methodName, String methodArguments) { // There is a hard limit of 100k in the Jython source code parser try { // Write message to temp file - File tempFile = File - .createTempFile("Selenium2Library-", ".log"); + File tempFile = File.createTempFile("Selenium2Library-", ".log"); tempFile.deleteOnExit(); FileWriter writer = new FileWriter(tempFile); writer.write(msg); @@ -83,53 +348,37 @@ protected void log0(String msg, String methodName, String methodArguments) { // Read the message in Python back and log it. loggingPythonInterpreter.get().exec( - String.format("from __future__ import with_statement\n" - + "\n" + "with open('%s', 'r') as msg_file:\n" - + " msg = msg_file.read()\n" - + " logger.%s(msg%s)", tempFile - .getAbsolutePath().replace("\\", "\\\\"), - methodName, methodArguments)); + String.format("from __future__ import with_statement\n" + "\n" + + "with open('%s', 'r') as msg_file:\n" + " msg = msg_file.read()\n" + + " logger.%s(msg%s)", tempFile.getAbsolutePath().replace("\\", "\\\\"), methodName, + methodArguments)); } catch (IOException e) { - throw new Selenium2LibraryNonFatalException( - "Error in handling temp file for long log message.", e); + throw new Selenium2LibraryNonFatalException("Error in handling temp file for long log message.", e); } } else { // Message is small enough to get parsed by Jython loggingPythonInterpreter.get().exec( - String.format("logger.%s('%s'%s)", methodName, - msg.replace("'", "\\'").replace("\n", "\\n"), - methodArguments)); - } - } - - @Override - protected List logList(List items) { - return logList(items, "item"); - } - - @Override - protected List logList(List items, String what) { - List msg = new ArrayList(); - msg.add(String.format("Altogether %d %s%s.\n", items.size(), what, - items.size() == 1 ? "" : "s")); - for (int index = 0; index < items.size(); index++) { - msg.add(String.format("%d: %s", index + 1, items.get(index))); + String.format("logger.%s('%s'%s)", methodName, msg.replace("\\", "\\\\").replace("'", "\\'") + .replace("\n", "\\n"), methodArguments)); } - info(Python.join("\n", msg)); - return items; } - @Override protected File getLogDir() { - PyString logDirName = (PyString) loggingPythonInterpreter.get().eval( - "GLOBAL_VARIABLES['${LOG FILE}']"); - if (logDirName != null && !(logDirName.asString().equals("NONE"))) { + if (logDir == null) { + PyString logDirName = (PyString) loggingPythonInterpreter.get().eval("GLOBAL_VARIABLES['${LOG FILE}']"); + if (logDirName != null && !(logDirName.asString().toUpperCase().equals("NONE"))) { + return new File(logDirName.asString()).getParentFile(); + } + logDirName = (PyString) loggingPythonInterpreter.get().eval("GLOBAL_VARIABLES['${OUTPUTDIR}']"); return new File(logDirName.asString()).getParentFile(); + } else { + return new File(logDir); } - logDirName = (PyString) loggingPythonInterpreter.get().eval( - "GLOBAL_VARIABLES['${OUTPUTDIR}']"); - return new File(logDirName.asString()).getParentFile(); + } + + public static void setLogDir(String logDirectory) { + logDir = logDirectory; } protected static ThreadLocal loggingPythonInterpreter = new ThreadLocal() { @@ -137,8 +386,7 @@ protected File getLogDir() { @Override protected PythonInterpreter initialValue() { PythonInterpreter pythonInterpreter = new PythonInterpreter(); - pythonInterpreter - .exec("from robot.variables import GLOBAL_VARIABLES; from robot.api import logger;"); + pythonInterpreter.exec("from robot.variables import GLOBAL_VARIABLES; from robot.api import logger;"); return pythonInterpreter; } }; diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/RunOnFailure.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/RunOnFailure.java index 37e85d5..25764c1 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/RunOnFailure.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/RunOnFailure.java @@ -1,23 +1,91 @@ package com.github.markusbernhardt.selenium2library.keywords; import org.python.util.PythonInterpreter; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywords; -public abstract class RunOnFailure extends Logging { +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; + +@RobotKeywords +public class RunOnFailure extends RunOnFailureKeywordsAdapter { + + /** + * The keyword to run an failure + */ + protected String runOnFailureKeyword = "Capture Page Screenshot"; + + /** + * Only run keyword on failure if true + */ + protected boolean runningOnFailureRoutine; + + /** + * Instantiated Logging keyword bean + */ + @Autowired + protected Logging logging; // ############################## // Keywords // ############################## + /** + * Sets the actual and returns the previous keyword to execute when a + * Selenium2Library keyword fails.
+ *
+ * The keyword is the name of a keyword (from any available + * libraries) that will be executed, if a Selenium2Library keyword fails. It + * is not possible to use a keyword that requires arguments. Using the value + * Nothing will disable this feature altogether.
+ *
+ * The initial keyword to use is set at importing the library and the + * keyword that is used by default is `Capture Page Screenshot`. Taking a + * screenshot when something failed is a very useful feature, but notice + * that it can slow down the execution.
+ *
+ * This keyword returns the name of the previously registered failure + * keyword. It can be used to restore the original value later.
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Register Keyword To Run On FailureLog Source# Run `Log Source` on failure.
${previous kw}=Register Keyword To Run On FailureNothing# Disable run-on-failure functionality and stors the previous kw name + * in a variable.
Register Keyword To Run On Failure${previous kw}# Restore to the previous keyword.
+ * + * @param keyword + * The keyword to execute on failure + * @return The previous keyword + */ + @RobotKeyword + @ArgumentNames({ "keyword" }) public String registerKeywordToRunOnFailure(String keyword) { String oldKeyword = runOnFailureKeyword; String oldKeywordText = oldKeyword != null ? oldKeyword : "No keyword"; - String newKeyword = !keyword.trim().toLowerCase().equals("nothing") ? keyword - : null; + String newKeyword = !keyword.trim().toLowerCase().equals("nothing") ? keyword : null; String newKeywordText = newKeyword != null ? newKeyword : "No keyword"; runOnFailureKeyword = newKeyword; - info(String.format("%s will be run on failure.", newKeywordText)); + logging.info(String.format("%s will be run on failure.", newKeywordText)); return oldKeywordText; } @@ -26,24 +94,17 @@ public String registerKeywordToRunOnFailure(String keyword) { // Internal Methods // ############################## - /** - * The keyword to run an failure - */ - protected String runOnFailureKeyword = "Capture Page Screenshot"; - protected boolean runningOnFailureRoutine; - protected static ThreadLocal runOnFailurePythonInterpreter = new ThreadLocal() { @Override protected PythonInterpreter initialValue() { PythonInterpreter pythonInterpreter = new PythonInterpreter(); - pythonInterpreter - .exec("from robot.libraries import BuiltIn; BUILTIN = BuiltIn.BuiltIn();"); + pythonInterpreter.exec("from robot.libraries import BuiltIn; BUILTIN = BuiltIn.BuiltIn();"); return pythonInterpreter; } }; - protected void runOnFailure() { + public void runOnFailure() { if (runOnFailureKeyword == null) { return; } @@ -53,13 +114,10 @@ protected void runOnFailure() { runningOnFailureRoutine = true; try { runOnFailurePythonInterpreter.get().exec( - String.format( - "BUILTIN.run_keyword('%s')", - runOnFailureKeyword.replace("'", "\\'").replace( - "\n", "\\n"))); + String.format("BUILTIN.run_keyword('%s')", + runOnFailureKeyword.replace("'", "\\'").replace("\n", "\\n"))); } catch (RuntimeException r) { - warn(String.format("Keyword '%s' could not be run on failure%s", - runOnFailureKeyword, + logging.warn(String.format("Keyword '%s' could not be run on failure%s", runOnFailureKeyword, r.getMessage() != null ? " '" + r.getMessage() + "'" : "")); } finally { runningOnFailureRoutine = false; diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Screenshot.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Screenshot.java index 9d7c9fa..9be848d 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Screenshot.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Screenshot.java @@ -6,70 +6,104 @@ import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywordOverload; +import org.robotframework.javalib.annotation.RobotKeywords; +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; import com.github.markusbernhardt.selenium2library.utils.Robotframework; -public abstract class Screenshot extends RunOnFailure { +@RobotKeywords +public class Screenshot extends RunOnFailureKeywordsAdapter { + + /** + * Instantiated BrowserManagement keyword bean + */ + @Autowired + protected BrowserManagement browserManagement; + + /** + * Instantiated Logging keyword bean + */ + @Autowired + protected Logging logging; // ############################## // Keywords // ############################## - + @RobotKeywordOverload public void capturePageScreenshot() { capturePageScreenshot(null); } + /** + * Take a screenshot of the current page and embed it into the log.
+ *
+ * The filename argument specifies the name of the file to write the + * screenshot into. If no filename is given, the screenshot is saved into + * file selenium-screenshot-<counter>.png under the directory where + * the Robot Framework log file is written into. The filename is also + * considered relative to the same directory, if it is not given in absolute + * format.
+ *
+ * A CSS can be used to modify how the screenshot is taken. By default the + * background color is changed to avoid possible problems with background + * leaking when the page layout is somehow broken.
+ * + * @param filename + * Default=NONE. Name of the file to write. + */ + @RobotKeyword + @ArgumentNames({ "filename=NONE" }) public void capturePageScreenshot(String filename) { - File logdir = getLogDir(); + File logdir = logging.getLogDir(); File path = new File(logdir, normalizeFilename(filename)); String link = Robotframework.getLinkPath(path, logdir); - TakesScreenshot takesScreenshot = ((TakesScreenshot) webDriverCache - .getCurrent()); + TakesScreenshot takesScreenshot = ((TakesScreenshot) browserManagement.getCurrentWebDriver()); if (takesScreenshot == null) { - warn("Can't take screenshot. No open browser found"); + logging.warn("Can't take screenshot. No open browser found"); return; } byte[] png = takesScreenshot.getScreenshotAs(OutputType.BYTES); writeScreenshot(path, png); - html(String - .format("", - link, link)); + logging.html(String.format( + "", link, link)); } // ############################## // Internal Methods // ############################## - private int screenshotIndex = 0; + protected int screenshotIndex = 0; - private void writeScreenshot(File path, byte[] png) { + protected void writeScreenshot(File path, byte[] png) { FileOutputStream fos = null; try { fos = new FileOutputStream(path); fos.write(png); fos.flush(); } catch (IOException e) { - warn(String.format("Can't write screenshot '%s'", - path.getAbsolutePath())); + logging.warn(String.format("Can't write screenshot '%s'", path.getAbsolutePath())); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { - warn("Can't even close stream"); + logging.warn("Can't even close stream"); } } } } - private String normalizeFilename(String filename) { + protected String normalizeFilename(String filename) { if (filename == null) { screenshotIndex++; - filename = String.format("selenium-screenshot-%d.png", - screenshotIndex); + filename = String.format("selenium-screenshot-%d.png", screenshotIndex); } else { filename = filename.replace('/', File.separatorChar); } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/SelectElement.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/SelectElement.java index 250dc92..41aaefb 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/SelectElement.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/SelectElement.java @@ -6,130 +6,293 @@ import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.Select; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywordOverload; +import org.robotframework.javalib.annotation.RobotKeywords; +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; import com.github.markusbernhardt.selenium2library.utils.Python; -public abstract class SelectElement extends Screenshot { +@RobotKeywords +public class SelectElement extends RunOnFailureKeywordsAdapter { + + /** + * Instantiated Element keyword bean + */ + @Autowired + protected Element element; + + /** + * Instantiated Logging keyword bean + */ + @Autowired + protected Logging logging; // ############################## // Keywords // ############################## + /** + * Returns the values in the select list identified by locator.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the select list. + * @return The select list values + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public List getListItems(String locator) { - List options = this.getSelectListOptions(locator); + List options = getSelectListOptions(locator); return getLabelsForOptions(options); } + /** + * Returns the visible label of the first selected element from the select + * list identified by locator.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the select list. + * @return The first visible select list label + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public String getSelectedListLabel(String locator) { Select select = getSelectList(locator); return select.getFirstSelectedOption().getText(); } + /** + * Returns the visible labels of the first selected elements as a list from + * the select list identified by locator.
+ *
+ * Fails if there is no selection.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the select list. + * @return The list of visible select list labels + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public List getSelectedListLabels(String locator) { List options = getSelectListOptionsSelected(locator); if (options.size() == 0) { - throw new Selenium2LibraryNonFatalException( - String.format( - "Select list with locator '%s' does not have any selected values.", - locator)); + throw new Selenium2LibraryNonFatalException(String.format( + "Select list with locator '%s' does not have any selected values.", locator)); } return getLabelsForOptions(options); } + /** + * Returns the value of the first selected element from the select list + * identified by locator.
+ *
+ * The return value is read from the value attribute of the selected + * element.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the select list. + * @return The first select list value + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public String getSelectedListValue(String locator) { Select select = getSelectList(locator); return select.getFirstSelectedOption().getAttribute("value"); } + /** + * Returns the values of the first selected elements as a list from the + * select list identified by locator.
+ *
+ * Fails if there is no selection. The return values are read from the value + * attribute of the selected element.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the select list. + * @return The list of select list values + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public List getSelectedListValues(String locator) { List options = getSelectListOptionsSelected(locator); if (options.size() == 0) { - throw new Selenium2LibraryNonFatalException( - String.format( - "Select list with locator '%s' does not have any selected values.", - locator)); + throw new Selenium2LibraryNonFatalException(String.format( + "Select list with locator '%s' does not have any selected values.", locator)); } return getValuesForOptions(options); } + /** + * Verify the selection of the select list identified by locatoris + * exactly *items.
+ *
+ * If you want to verify no option is selected, simply give no items.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the select list. + * @param items + * The list of items to verify + */ + @RobotKeyword + @ArgumentNames({ "locator", "*items" }) public void listSelectionShouldBe(String locator, String... items) { - String itemList = items.length != 0 ? String.format("option(s) [ %s ]", - Python.join(" | ", items)) : "no options"; - info(String.format("Verifying list '%s' has %s selected.", locator, - itemList)); + String itemList = items.length != 0 ? String.format("option(s) [ %s ]", Python.join(" | ", items)) + : "no options"; + logging.info(String.format("Verifying list '%s' has %s selected.", locator, itemList)); - this.pageShouldContainList(locator); + pageShouldContainList(locator); List options = getSelectListOptionsSelected(locator); List selectedLabels = getLabelsForOptions(options); - String message = String - .format("List '%s' should have had selection [ %s ] but it was [ %s ].", - locator, Python.join(" | ", items), - Python.join(" | ", selectedLabels)); + String message = String.format("List '%s' should have had selection [ %s ] but it was [ %s ].", locator, + Python.join(" | ", items), Python.join(" | ", selectedLabels)); if (items.length != options.size()) { throw new Selenium2LibraryNonFatalException(message); } else { List selectedValues = getValuesForOptions(options); for (String item : items) { - if (!selectedValues.contains(item) - && !selectedLabels.contains(item)) { + if (!selectedValues.contains(item) && !selectedLabels.contains(item)) { throw new Selenium2LibraryNonFatalException(message); } } } } + /** + * Verify the select list identified by locatorhas no selections.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the select list. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void listShouldHaveNoSelections(String locator) { - info(String.format("Verifying list '%s' has no selection.", locator)); + logging.info(String.format("Verifying list '%s' has no selection.", locator)); List options = getSelectListOptionsSelected(locator); if (!options.equals(null)) { List selectedLabels = getLabelsForOptions(options); String items = Python.join(" | ", selectedLabels); - throw new Selenium2LibraryNonFatalException( - String.format( - "List '%s' should have had no selection (selection was [ %s ]).", - locator, items.toString())); + throw new Selenium2LibraryNonFatalException(String.format( + "List '%s' should have had no selection (selection was [ %s ]).", locator, items.toString())); } } + @RobotKeywordOverload public void pageShouldContainList(String locator) { pageShouldContainList(locator, ""); } + @RobotKeywordOverload public void pageShouldContainList(String locator, String message) { pageShouldContainList(locator, message, "INFO"); } - public void pageShouldContainList(String locator, String message, - String logLevel) { - this.pageShouldContainElement(locator, "list", message, logLevel); + /** + * Verify the select list identified by locator is found on the + * current page.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators and log levels.
+ * + * @param locator + * The locator to locate the select list. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldContainList(String locator, String message, String logLevel) { + element.pageShouldContainElement(locator, "list", message, logLevel); } + @RobotKeywordOverload public void pageShouldNotContainList(String locator) { pageShouldNotContainList(locator, ""); } + @RobotKeywordOverload public void pageShouldNotContainList(String locator, String message) { pageShouldNotContainList(locator, message, "INFO"); } - public void pageShouldNotContainList(String locator, String message, - String logLevel) { - this.pageShouldNotContainElement(locator, "list", message, logLevel); + /** + * Verify the select list identified by locator is not found on the + * current page.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators and log levels.
+ * + * @param locator + * The locator to locate the select list. + * @param message + * Default=NONE. Optional custom error message. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) + public void pageShouldNotContainList(String locator, String message, String logLevel) { + element.pageShouldNotContainElement(locator, "list", message, logLevel); } + /** + * Select all values of the multi-select list identified by locator.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the multi-select list. + */ + @RobotKeyword + @ArgumentNames({ "locator" }) public void selectAllFromList(String locator) { - info(String.format("Selecting all options from list '%s'.", locator)); + logging.info(String.format("Selecting all options from list '%s'.", locator)); Select select = getSelectList(locator); if (!isMultiselectList(select)) { @@ -142,10 +305,30 @@ public void selectAllFromList(String locator) { } } + /** + * Select the given *items of the multi-select list identified by + * locator.
+ *
+ * An exception is raised for a single-selection list if the last value does + * not exist in the list and a warning for all other non-existing items. For + * a multi-selection list, an exception is raised for any and all + * non-existing values.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the multi-select list. + * @param items + * The list of items to select + */ + @RobotKeyword + @ArgumentNames({ "locator", "*items" }) public void selectFromList(String locator, String... items) { - String itemList = items.length != 0 ? String.format("option(s) [ %s ]", - Python.join(" | ", items)) : "all options"; - info(String.format("Selecting %s from list '%s'.", itemList, locator)); + String itemList = items.length != 0 ? String.format("option(s) [ %s ]", Python.join(" | ", items)) + : "all options"; + logging.info(String.format("Selecting %s from list '%s'.", itemList, locator)); Select select = getSelectList(locator); @@ -155,21 +338,63 @@ public void selectFromList(String locator, String... items) { for (int i = 0; i < select.getOptions().size(); i++) { select.selectByIndex(i); } + return; } + boolean lastItemFound = false; + List nonExistingItems = new ArrayList(); for (String item : items) { + lastItemFound = true; try { select.selectByValue(item); } catch (NoSuchElementException e1) { try { select.selectByVisibleText(item); } catch (NoSuchElementException e2) { + nonExistingItems.add(item); + lastItemFound = false; continue; } } } + + if (nonExistingItems.size() != 0) { + // multi-selection list => throw immediately + if (select.isMultiple()) { + throw new Selenium2LibraryNonFatalException(String.format("Options '%s' not in list '%s'.", + Python.join(", ", nonExistingItems), locator)); + } + + // single-selection list => log warning with not found items + logging.warn(String.format("Option%s '%s' not found within list '%s'.", nonExistingItems.size() == 0 ? "" + : "s", Python.join(", ", nonExistingItems), locator)); + + // single-selection list => throw if last item was not found + if (!lastItemFound) { + throw new Selenium2LibraryNonFatalException(String.format("Option '%s' not in list '%s'.", + nonExistingItems.get(nonExistingItems.size() - 1), locator)); + } + } } + /** + * Select the given *indexes of the multi-select list identified by + * locator.
+ *
+ * Tries to select by value AND by label. It's generally faster to use 'by + * index/value/label' keywords.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the multi-select list. + * @param indexes + * The list of indexes to select + */ + @RobotKeyword + @ArgumentNames({ "locator", "*indexes" }) public void selectFromListByIndex(String locator, String... indexes) { if (indexes.length == 0) { throw new Selenium2LibraryNonFatalException("No index given."); @@ -180,7 +405,7 @@ public void selectFromListByIndex(String locator, String... indexes) { tmp.add(index); } String items = String.format("index(es) '%s'", Python.join(", ", tmp)); - info(String.format("Selecting %s from list '%s'.", items, locator)); + logging.info(String.format("Selecting %s from list '%s'.", items, locator)); Select select = getSelectList(locator); for (String index : indexes) { @@ -188,14 +413,28 @@ public void selectFromListByIndex(String locator, String... indexes) { } } + /** + * Select the given *values of the multi-select list identified by + * locator.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the multi-select list. + * @param values + * The list of values to select + */ + @RobotKeyword + @ArgumentNames({ "locator", "*values" }) public void selectFromListByValue(String locator, String... values) { if (values.length == 0) { throw new Selenium2LibraryNonFatalException("No value given."); } - String items = String - .format("value(s) '%s'", Python.join(", ", values)); - info(String.format("Selecting %s from list '%s'.", items, locator)); + String items = String.format("value(s) '%s'", Python.join(", ", values)); + logging.info(String.format("Selecting %s from list '%s'.", items, locator)); Select select = getSelectList(locator); for (String value : values) { @@ -203,14 +442,28 @@ public void selectFromListByValue(String locator, String... values) { } } + /** + * Select the given *labels of the multi-select list identified by + * locator.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the multi-select list. + * @param labels + * The list of labels to select + */ + @RobotKeyword + @ArgumentNames({ "locator", "*labels" }) public void selectFromListByLabel(String locator, String... labels) { if (labels.length == 0) { throw new Selenium2LibraryNonFatalException("No value given."); } - String items = String - .format("label(s) '%s'", Python.join(", ", labels)); - info(String.format("Selecting %s from list '%s'.", items, locator)); + String items = String.format("label(s) '%s'", Python.join(", ", labels)); + logging.info(String.format("Selecting %s from list '%s'.", items, locator)); Select select = getSelectList(locator); for (String label : labels) { @@ -218,10 +471,31 @@ public void selectFromListByLabel(String locator, String... labels) { } } + /** + * Unselect the given *items of the multi-select list identified by + * locator.
+ *
+ * As a special case, giving an empty *items list will remove all + * selections.
+ *
+ * Tries to unselect by value AND by label. It's generally faster to use 'by + * index/value/label' keywords.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the multi-select list. + * @param items + * The list of items to select + */ + @RobotKeyword + @ArgumentNames({ "locator", "*items" }) public void unselectFromList(String locator, String... items) { - String itemList = items.length != 0 ? String.format("option(s) [ %s ]", - Python.join(" | ", items)) : "all options"; - info(String.format("Unselecting %s from list '%s'.", itemList, locator)); + String itemList = items.length != 0 ? String.format("option(s) [ %s ]", Python.join(" | ", items)) + : "all options"; + logging.info(String.format("Unselecting %s from list '%s'.", itemList, locator)); Select select = getSelectList(locator); @@ -242,6 +516,21 @@ public void unselectFromList(String locator, String... items) { } } + /** + * Unselect the given *indexes of the multi-select list identified by + * locator.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the multi-select list. + * @param indexes + * The list of indexes to select + */ + @RobotKeyword + @ArgumentNames({ "locator", "*indexes" }) public void unselectFromListByIndex(String locator, Integer... indexes) { if (indexes.equals(null)) { throw new Selenium2LibraryNonFatalException("No index given."); @@ -252,7 +541,7 @@ public void unselectFromListByIndex(String locator, Integer... indexes) { tmp.add(index.toString()); } String items = String.format("index(es) '%s'", Python.join(", ", tmp)); - info(String.format("Unselecting %s from list '%s'.", items, locator)); + logging.info(String.format("Unselecting %s from list '%s'.", items, locator)); Select select = getSelectList(locator); @@ -266,14 +555,28 @@ public void unselectFromListByIndex(String locator, Integer... indexes) { } } + /** + * Unselect the given *values of the multi-select list identified by + * locator.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the multi-select list. + * @param values + * The list of values to select + */ + @RobotKeyword + @ArgumentNames({ "locator", "*values" }) public void unselectFromListByValue(String locator, String... values) { if (values.equals(null)) { throw new Selenium2LibraryNonFatalException("No value given."); } - String items = String - .format("value(s) '%s'", Python.join(", ", values)); - info(String.format("Unselecting %s from list '%s'.", items, locator)); + String items = String.format("value(s) '%s'", Python.join(", ", values)); + logging.info(String.format("Unselecting %s from list '%s'.", items, locator)); Select select = getSelectList(locator); @@ -287,14 +590,28 @@ public void unselectFromListByValue(String locator, String... values) { } } + /** + * Unselect the given *labels of the multi-select list identified by + * locator.
+ *
+ * Select list keywords work on both lists and combo boxes. Key attributes + * for select lists are id and name. See `Introduction` for details about + * locators.
+ * + * @param locator + * The locator to locate the multi-select list. + * @param labels + * The list of labels to select + */ + @RobotKeyword + @ArgumentNames({ "locator", "*labels" }) public void unselectFromListByLabel(String locator, String... labels) { if (labels.equals(null)) { throw new Selenium2LibraryNonFatalException("No value given."); } - String items = String - .format("label(s) '%s'", Python.join(", ", labels)); - info(String.format("Unselecting %s from list '%s'.", items, locator)); + String items = String.format("label(s) '%s'", Python.join(", ", labels)); + logging.info(String.format("Unselecting %s from list '%s'.", items, locator)); Select select = getSelectList(locator); @@ -312,7 +629,7 @@ public void unselectFromListByLabel(String locator, String... labels) { // Internal Methods // ############################## - private List getLabelsForOptions(List options) { + protected List getLabelsForOptions(List options) { List labels = new ArrayList(); for (WebElement option : options) { @@ -322,29 +639,29 @@ private List getLabelsForOptions(List options) { return labels; } - private Select getSelectList(String locator) { - List elements = elementFind(locator, true, true, "select"); + protected Select getSelectList(String locator) { + List webElements = element.elementFind(locator, true, true, "select"); - return new Select(elements.get(0)); + return new Select(webElements.get(0)); } - private List getSelectListOptions(Select select) { + protected List getSelectListOptions(Select select) { return new ArrayList(select.getOptions()); } - private List getSelectListOptions(String locator) { + protected List getSelectListOptions(String locator) { Select select = getSelectList(locator); return getSelectListOptions(select); } - private List getSelectListOptionsSelected(String locator) { + protected List getSelectListOptionsSelected(String locator) { Select select = getSelectList(locator); return new ArrayList(select.getAllSelectedOptions()); } - private List getValuesForOptions(List options) { + protected List getValuesForOptions(List options) { ArrayList labels = new ArrayList(); for (WebElement option : options) { @@ -354,8 +671,8 @@ private List getValuesForOptions(List options) { return labels; } - private boolean isMultiselectList(Select select) { + protected boolean isMultiselectList(Select select) { return select.isMultiple(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Selenium2LibraryEnhancement.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Selenium2LibraryEnhancement.java deleted file mode 100644 index abd7a40..0000000 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Selenium2LibraryEnhancement.java +++ /dev/null @@ -1,517 +0,0 @@ -package com.github.markusbernhardt.selenium2library.keywords; - -import java.util.List; - -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; - -import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; -import com.github.markusbernhardt.selenium2library.locators.ElementFinder; - -public class Selenium2LibraryEnhancement extends Waiting { - - // ############################## - // Keywords - // ############################## - - public String getSystemInfo() { - return String - .format(" os.name: '%s'\n os.arch: '%s'\n os.version: '%s'\n java.version: '%s'", - System.getProperty("os.name"), - System.getProperty("os.arch"), - System.getProperty("os.version"), - System.getProperty("java.version")); - } - - public String logSystemInfo() { - String actual = getSystemInfo(); - info(actual); - return actual; - } - - public String getRemoteCapabilities() { - if (webDriverCache.getCurrent() instanceof RemoteWebDriver) { - return ((RemoteWebDriver) webDriverCache.getCurrent()) - .getCapabilities().toString(); - } else { - return "No remote session id"; - } - } - - public String logRemoteCapabilities() { - String actual = getRemoteCapabilities(); - info(actual); - return actual; - } - - public String getRemoteSessionId() { - if (webDriverCache.getCurrent() instanceof RemoteWebDriver) { - return ((RemoteWebDriver) webDriverCache.getCurrent()) - .getSessionId().toString(); - } else { - return "No remote session id"; - } - } - - public String logRemoteSessionId() { - String actual = getRemoteSessionId(); - info(actual); - return actual; - } - - public void addLocationStrategy(String strategyName, - String functionDefinition) { - addLocationStrategy(strategyName, functionDefinition, null); - } - - public void addLocationStrategy(String strategyName, - String functionDefinition, String delimiter) { - ElementFinder.addLocationStrategy(strategyName, functionDefinition, - delimiter); - } - - public void waitUntilPageNotContains(String condition) { - waitUntilPageNotContains(condition, null); - } - - public void waitUntilPageNotContains(String condition, String timestr) { - waitUntilPageNotContains(condition, timestr, null); - } - - public void waitUntilPageNotContains(final String text, String timestr, - String error) { - if (error == null) { - error = String.format("Text '%s' did not disappear in ", - text); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - return !isTextPresent(text); - } - }); - } - - public void waitUntilPageNotContainsElement(String locator) { - waitUntilPageNotContainsElement(locator, null); - } - - public void waitUntilPageNotContainsElement(String locator, String timestr) { - waitUntilPageNotContainsElement(locator, timestr, null); - } - - public void waitUntilPageNotContainsElement(final String locator, - String timestr, String error) { - if (error == null) { - error = String.format( - "Element '%s' did not disappear in ", locator); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - return !isElementPresent(locator); - } - }); - } - - public void waitUntilElementIsVisible(String locator) { - waitUntilElementIsVisible(locator, null); - } - - public void waitUntilElementIsVisible(String locator, String timestr) { - waitUntilElementIsVisible(locator, timestr, null); - } - - public void waitUntilElementIsVisible(final String locator, String timestr, - String error) { - if (error == null) { - error = String.format("Element '%s' not visible in ", - locator); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - return isVisible(locator); - } - }); - } - - public void waitUntilElementIsNotVisible(String locator) { - waitUntilElementIsNotVisible(locator, null); - } - - public void waitUntilElementIsNotVisible(String locator, String timestr) { - waitUntilElementIsNotVisible(locator, timestr, null); - } - - public void waitUntilElementIsNotVisible(final String locator, - String timestr, String error) { - if (error == null) { - error = String.format("Element '%s' still visible in ", - locator); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - return !isVisible(locator); - } - }); - } - - public void waitUntilTitleContains(String title) { - waitUntilTitleContains(title, null); - } - - public void waitUntilTitleContains(String title, String timestr) { - waitUntilTitleContains(title, timestr, null); - } - - public void waitUntilTitleContains(final String title, String timestr, - String error) { - if (error == null) { - error = String.format("Title '%s' did not appear in ", - title); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - String currentTitle = getTitle(); - return currentTitle != null && currentTitle.contains(title); - } - }); - } - - public void waitUntilTitleNotContains(String title) { - waitUntilTitleNotContains(title, null); - } - - public void waitUntilTitleNotContains(String title, String timestr) { - waitUntilTitleNotContains(title, timestr, null); - } - - public void waitUntilTitleNotContains(final String title, String timestr, - String error) { - if (error == null) { - error = String.format("Title '%s' did not appear in ", - title); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - String currentTitle = getTitle(); - return currentTitle == null || !currentTitle.contains(title); - } - }); - } - - public void waitUntilTitleIs(String title) { - waitUntilTitleIs(title, null); - } - - public void waitUntilTitleIs(String title, String timestr) { - waitUntilTitleIs(title, timestr, null); - } - - public void waitUntilTitleIs(final String title, String timestr, - String error) { - if (error == null) { - error = String.format("Title '%s' did not appear in ", - title); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - String currentTitle = getTitle(); - return currentTitle != null && currentTitle.equals(title); - } - }); - } - - public void waitUntilTitleIsNot(String title) { - waitUntilTitleIsNot(title, null); - } - - public void waitUntilTitleIsNot(String title, String timestr) { - waitUntilTitleIsNot(title, timestr, null); - } - - public void waitUntilTitleIsNot(final String title, String timestr, - String error) { - if (error == null) { - error = String.format("Title '%s' did not appear in ", - title); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - String currentTitle = getTitle(); - return currentTitle == null || !currentTitle.equals(title); - } - }); - } - - public void elementShouldBeSelected(String locator) { - this.elementShouldBeSelected(locator, ""); - } - - public void elementShouldBeSelected(String locator, String message) { - info(String.format("Verifying element '%s' is selected.", locator)); - boolean selected = isSelected(locator); - - if (!selected) { - if (message == null || message.equals("")) { - message = String.format( - "Element '%s' should be selected, but it is not.", - locator); - } - throw new Selenium2LibraryNonFatalException(message); - } - } - - public void elementShouldNotBeSelected(String locator) { - this.elementShouldNotBeSelected(locator, ""); - } - - public void elementShouldNotBeSelected(String locator, String message) { - info(String.format("Verifying element '%s' is not selected.", locator)); - boolean selected = isSelected(locator); - - if (selected) { - if (message == null || message.equals("")) { - message = String.format( - "Element '%s' should not be selected, but it is.", - locator); - } - throw new Selenium2LibraryNonFatalException(message); - } - } - - public void waitUntilElementIsSelected(String locator) { - waitUntilElementIsSelected(locator, null); - } - - public void waitUntilElementIsSelected(String locator, String timestr) { - waitUntilElementIsSelected(locator, timestr, null); - } - - public void waitUntilElementIsSelected(final String locator, - String timestr, String error) { - if (error == null) { - error = String.format("Element '%s' not selected in ", - locator); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - return isSelected(locator); - } - }); - } - - public void waitUntilElementIsNotSelected(String locator) { - waitUntilElementIsNotSelected(locator, null); - } - - public void waitUntilElementIsNotSelected(String locator, String timestr) { - waitUntilElementIsNotSelected(locator, timestr, null); - } - - public void waitUntilElementIsNotSelected(final String locator, - String timestr, String error) { - if (error == null) { - error = String.format("Element '%s' still selected in ", - locator); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - return !isSelected(locator); - } - }); - } - - public void elementShouldBeClickable(String locator) { - this.elementShouldBeClickable(locator, ""); - } - - public void elementShouldBeClickable(String locator, String message) { - info(String.format("Verifying element '%s' is clickable.", locator)); - boolean clickable = isClickable(locator); - - if (!clickable) { - if (message == null || message.equals("")) { - message = String.format( - "Element '%s' should be clickable, but it is not.", - locator); - } - throw new Selenium2LibraryNonFatalException(message); - } - } - - public void elementShouldNotBeClickable(String locator) { - this.elementShouldNotBeClickable(locator, ""); - } - - public void elementShouldNotBeClickable(String locator, String message) { - info(String.format("Verifying element '%s' is not clickable.", locator)); - boolean clickable = isClickable(locator); - - if (clickable) { - if (message == null || message.equals("")) { - message = String.format( - "Element '%s' should not be clickable, but it is.", - locator); - } - throw new Selenium2LibraryNonFatalException(message); - } - } - - public void waitUntilElementIsClickable(String locator) { - waitUntilElementIsClickable(locator, null); - } - - public void waitUntilElementIsClickable(String locator, String timestr) { - waitUntilElementIsClickable(locator, timestr, null); - } - - public void waitUntilElementIsClickable(final String locator, - String timestr, String error) { - if (error == null) { - error = String.format("Element '%s' not clickable in ", - locator); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - return isClickable(locator); - } - }); - } - - public void waitUntilElementIsSuccessfullyClicked(String locator) { - waitUntilElementIsSuccessfullyClicked(locator, null); - } - - public void waitUntilElementIsSuccessfullyClicked(String locator, - String timestr) { - waitUntilElementIsSuccessfullyClicked(locator, timestr, null); - } - - public void waitUntilElementIsSuccessfullyClicked(final String locator, - String timestr, String error) { - if (error == null) { - error = String.format( - "Element '%s' not successfully clicked in ", - locator); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - clickElement(locator); - return true; - } - }); - } - - public void waitUntilElementIsNotClickable(String locator) { - waitUntilElementIsNotClickable(locator, null); - } - - public void waitUntilElementIsNotClickable(String locator, String timestr) { - waitUntilElementIsNotClickable(locator, timestr, null); - } - - public void waitUntilElementIsNotClickable(final String locator, - String timestr, String error) { - if (error == null) { - error = String.format("Element '%s' still clickable in ", - locator); - } - waitUntil(timestr, error, new WaitUntilFunction() { - - @Override - public boolean isFinished() { - return !isClickable(locator); - } - }); - } - - public void elementShouldNotContain(String locator, String expected) { - this.elementShouldNotContain(locator, expected, ""); - } - - public void elementShouldNotContain(String locator, String expected, - String message) { - String actual = fetchText(locator); - - if (actual.toLowerCase().contains(expected.toLowerCase())) { - info(String.format("Element Should Not Contain: %s => FAILED", - expected)); - throw new Selenium2LibraryNonFatalException( - String.format( - "Element should not have contained text '%s' but its text was %s.", - expected, actual)); - } else { - info(String - .format("Element Should Not Contain: %s => OK", expected)); - } - } - - public void elementTextShouldNotBe(String locator, String expected) { - this.elementTextShouldNotBe(locator, expected, ""); - } - - public void elementTextShouldNotBe(String locator, String expected, - String message) { - info(String.format( - "Verifying element '%s' contains exactly text '%s'.", locator, - expected)); - - List elements = elementFind(locator, true, true); - String actual = elements.get(0).getText(); - - if (expected.equals(actual)) { - if (message == null || message.equals("")) { - message = String - .format("The text of element '%s' should have been '%s', but it was '%s'.", - locator, expected, actual); - } - throw new Selenium2LibraryNonFatalException(message); - } - } - - // ############################## - // Internal Methods - // ############################## - - protected boolean isClickable(String locator) { - List elements = elementFind(locator, true, false); - if (elements.size() == 0) { - return false; - } - WebElement element = elements.get(0); - return element.isDisplayed() && element.isEnabled(); - } - - protected boolean isSelected(String locator) { - List elements = elementFind(locator, true, false); - if (elements.size() == 0) { - return false; - } - WebElement element = elements.get(0); - return element.isSelected(); - } - -} diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/TableElement.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/TableElement.java index a3ffbce..74db4b5 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/TableElement.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/TableElement.java @@ -4,26 +4,67 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebElement; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywordOverload; +import org.robotframework.javalib.annotation.RobotKeywords; +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; import com.github.markusbernhardt.selenium2library.locators.TableElementFinder; -public abstract class TableElement extends SelectElement { +@RobotKeywords +public class TableElement extends RunOnFailureKeywordsAdapter { + + /** + * Instantiated BrowserManagement keyword bean + */ + @Autowired + protected BrowserManagement browserManagement; + + /** + * Instantiated Logging keyword bean + */ + @Autowired + protected Logging logging; // ############################## // Keywords // ############################## + @RobotKeywordOverload public String getTableCell(String tableLocator, int row, int column) { return getTableCell(tableLocator, row, column, "INFO"); } - public String getTableCell(String tableLocator, int row, int column, - String loglevel) { + /** + * Returns the content of the table cell at the coordinates row and + * column of the table identified by tableLocator.
+ *
+ * Row and column number start from 1. Header and footer rows are included + * in the count. That way also cell content from header or footer rows can + * be obtained with this keyword.
+ *
+ * Key attributes for tables are id and name. See `Introduction` for details + * about locators and log levels.
+ * + * @param tableLocator + * The locator to locate the table. + * @param row + * The table row. + * @param column + * The table column. + * @param logLevel + * Default=INFO. Optional log level. + * @return The table cell content. + */ + @RobotKeyword + @ArgumentNames({ "tableLocator", "row", "column", "logLevel=INFO" }) + public String getTableCell(String tableLocator, int row, int column, String logLevel) { int rowIndex = row - 1; int columnIndex = column - 1; - WebElement table = TableElementFinder.find(webDriverCache.getCurrent(), - tableLocator); + WebElement table = TableElementFinder.find(browserManagement.getCurrentWebDriver(), tableLocator); if (table != null) { List rows = table.findElements(By.xpath("./thead/tr")); if (rowIndex >= rows.size()) { @@ -33,134 +74,254 @@ public String getTableCell(String tableLocator, int row, int column, rows.addAll(table.findElements(By.xpath("./tfoot/tr"))); } if (rowIndex < rows.size()) { - List columns = rows.get(rowIndex).findElements( - By.tagName("th")); + List columns = rows.get(rowIndex).findElements(By.tagName("th")); if (columnIndex >= columns.size()) { - columns.addAll(rows.get(rowIndex).findElements( - By.tagName("td"))); + columns.addAll(rows.get(rowIndex).findElements(By.tagName("td"))); } if (columnIndex < columns.size()) { return columns.get(columnIndex).getText(); } } } - logSource(loglevel); - throw new Selenium2LibraryNonFatalException( - String.format( - "Cell in table %s in row #%d and column #%d could not be found.", - tableLocator, row, column)); + logging.logSource(logLevel); + throw new Selenium2LibraryNonFatalException(String.format( + "Cell in table %s in row #%d and column #%d could not be found.", tableLocator, row, column)); } - public void tableCellShouldContain(String tableLocator, int row, - int column, String expected) { - tableCellShouldContain(tableLocator, row, column, expected, "INFO"); + @RobotKeywordOverload + public void tableCellShouldContain(String tableLocator, int row, int column, String text) { + tableCellShouldContain(tableLocator, row, column, text, "INFO"); } - public void tableCellShouldContain(String tableLocator, int row, - int column, String expected, String loglevel) { - String message = String - .format("Cell in table '%s' in row #%d and column #%d should have contained text '%s'.", - tableLocator, row, column, expected); + /** + * Verify the content of the table cell at the coordinates row and + * column of the table identified by tableLocator contains + * text.
+ *
+ * Row and column number start from 1. Header and footer rows are included + * in the count. That way also cell content from header or footer rows can + * be obtained with this keyword.
+ *
+ * Key attributes for tables are id and name. See `Introduction` for details + * about locators and log levels.
+ * + * @param tableLocator + * The locator to locate the table. + * @param row + * The table row. + * @param column + * The table column. + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "tableLocator", "row", "column", "text", "logLevel=INFO" }) + public void tableCellShouldContain(String tableLocator, int row, int column, String text, String logLevel) { + String message = String.format("Cell in table '%s' in row #%d and column #%d should have contained text '%s'.", + tableLocator, row, column, text); String content = ""; try { - content = getTableCell(tableLocator, row, column, loglevel); - } catch (AssertionError err) { - info(err.getMessage()); + content = getTableCell(tableLocator, row, column, logLevel); + } catch (Selenium2LibraryNonFatalException e) { + logging.info(e.getMessage()); throw new Selenium2LibraryNonFatalException(message); } - info(String.format("Cell contains %s.", content)); - if (!content.contains(expected)) { - logSource(loglevel); + logging.info(String.format("Cell contains %s.", content)); + if (!content.contains(text)) { + logging.logSource(logLevel); throw new Selenium2LibraryNonFatalException(message); } } - public void tableColumnShouldContain(String tableLocator, int col, - String expected) { - tableColumnShouldContain(tableLocator, col, expected, "INFO"); + @RobotKeywordOverload + public void tableColumnShouldContain(String tableLocator, int col, String text) { + tableColumnShouldContain(tableLocator, col, text, "INFO"); } - public void tableColumnShouldContain(String tableLocator, int col, - String expected, String loglevel) { - WebElement element = TableElementFinder.findByCol( - webDriverCache.getCurrent(), tableLocator, col, expected); + /** + * Verify the content of any table cells of the table column of the + * table identified by tableLocator contains text.
+ *
+ * Key attributes for tables are id and name. See `Introduction` for details + * about locators and log levels.
+ *
+ * The first leftmost column is column number 1. If the table contains cells + * that span multiple columns, those merged cells count as a single column. + * For example both tests below work, if in one row columns A and B are + * merged with colspan="2", and the logical third column contains "C".
+ *
+ * Example: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Table Column Should ContaintableId3C
Table Column Should ContaintableId2C
+ * + * @param tableLocator + * The locator to locate the table. + * @param col + * The table column. + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "tableLocator", "col", "text", "logLevel=INFO" }) + public void tableColumnShouldContain(String tableLocator, int col, String text, String logLevel) { + WebElement element = TableElementFinder.findByCol(browserManagement.getCurrentWebDriver(), tableLocator, col, + text); if (element == null) { - logSource(loglevel); - throw new Selenium2LibraryNonFatalException( - String.format( - "Column #%d in table identified by '%s' should have contained text '%s'.", - col, tableLocator, expected)); + logging.logSource(logLevel); + throw new Selenium2LibraryNonFatalException(String.format( + "Column #%d in table identified by '%s' should have contained text '%s'.", col, tableLocator, text)); } } - public void tableFooterShouldContain(String tableLocator, String expected) { - tableFooterShouldContain(tableLocator, expected, "INFO"); + @RobotKeywordOverload + public void tableFooterShouldContain(String tableLocator, String text) { + tableFooterShouldContain(tableLocator, text, "INFO"); } - public void tableFooterShouldContain(String tableLocator, String expected, - String loglevel) { - WebElement element = TableElementFinder.findByFooter( - webDriverCache.getCurrent(), tableLocator, expected); + /** + * Verify the content of any table footer cells of the table identified by + * tableLocator contains text.
+ *
+ * Key attributes for tables are id and name. See `Introduction` for details + * about locators and log levels.
+ * + * @param tableLocator + * The locator to locate the table. + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "tableLocator", "text", "logLevel=INFO" }) + public void tableFooterShouldContain(String tableLocator, String text, String logLevel) { + WebElement element = TableElementFinder.findByFooter(browserManagement.getCurrentWebDriver(), tableLocator, + text); if (element == null) { - logSource(loglevel); - throw new Selenium2LibraryNonFatalException( - String.format( - "Footer in table identified by '%s' should have contained text '%s'.", - tableLocator, expected)); + logging.logSource(logLevel); + throw new Selenium2LibraryNonFatalException(String.format( + "Footer in table identified by '%s' should have contained text '%s'.", tableLocator, text)); } } - public void tableHeaderShouldContain(String tableLocator, String expected) { - tableHeaderShouldContain(tableLocator, expected, "INFO"); + @RobotKeywordOverload + public void tableHeaderShouldContain(String tableLocator, String text) { + tableHeaderShouldContain(tableLocator, text, "INFO"); } - public void tableHeaderShouldContain(String tableLocator, String expected, - String loglevel) { - WebElement element = TableElementFinder.findByHeader( - webDriverCache.getCurrent(), tableLocator, expected); + /** + * Verify the content of any table header cells of the table identified by + * tableLocator contains text.
+ *
+ * Key attributes for tables are id and name. See `Introduction` for details + * about locators and log levels.
+ * + * @param tableLocator + * The locator to locate the table. + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "tableLocator", "text", "logLevel=INFO" }) + public void tableHeaderShouldContain(String tableLocator, String text, String logLevel) { + WebElement element = TableElementFinder.findByHeader(browserManagement.getCurrentWebDriver(), tableLocator, + text); if (element == null) { - logSource(loglevel); - throw new Selenium2LibraryNonFatalException( - String.format( - "Header in table identified by '%s' should have contained text '%s'.", - tableLocator, expected)); + logging.logSource(logLevel); + throw new Selenium2LibraryNonFatalException(String.format( + "Header in table identified by '%s' should have contained text '%s'.", tableLocator, text)); } } - public void tableRowShouldContain(String tableLocator, int row, - String expected) { - tableRowShouldContain(tableLocator, row, expected, "INFO"); + @RobotKeywordOverload + public void tableRowShouldContain(String tableLocator, int row, String text) { + tableRowShouldContain(tableLocator, row, text, "INFO"); } - public void tableRowShouldContain(String tableLocator, int row, - String expected, String loglevel) { - WebElement element = TableElementFinder.findByRow( - webDriverCache.getCurrent(), tableLocator, row, expected); + /** + * Verify the content of any table cells of the table row of the + * table identified by tableLocator contains text.
+ *
+ * Key attributes for tables are id and name. See `Introduction` for details + * about locators and log levels.
+ *
+ * The uppermost row is row number 1. For tables that are structured with + * thead, tbody and tfoot, only the tbody section is searched. Please use + * Table Header Should Contain or Table Footer Should Contain for tests + * against the header or footer content.
+ *
+ * If the table contains cells that span multiple rows, a match only occurs + * for the uppermost row of those merged cells.
+ * + * @param tableLocator + * The locator to locate the table. + * @param row + * The table row. + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "tableLocator", "row", "text", "logLevel=INFO" }) + public void tableRowShouldContain(String tableLocator, int row, String text, String logLevel) { + WebElement element = TableElementFinder.findByRow(browserManagement.getCurrentWebDriver(), tableLocator, row, + text); if (element == null) { - logSource(loglevel); - throw new Selenium2LibraryNonFatalException( - String.format( - "Row #%d in table identified by '%s' should have contained text '%s'.", - row, tableLocator, expected)); + logging.logSource(logLevel); + throw new Selenium2LibraryNonFatalException(String.format( + "Row #%d in table identified by '%s' should have contained text '%s'.", row, tableLocator, text)); } } - public void tableShouldContain(String tableLocator, String expected) { - tableShouldContain(tableLocator, expected, "INFO"); + @RobotKeywordOverload + public void tableShouldContain(String tableLocator, String text) { + tableShouldContain(tableLocator, text, "INFO"); } - public void tableShouldContain(String tableLocator, String expected, - String loglevel) { - WebElement element = TableElementFinder.findByContent( - webDriverCache.getCurrent(), tableLocator, expected); + /** + * Verify the content of any table cells of the table identified by + * tableLocator contains text.
+ *
+ * Key attributes for tables are id and name. See `Introduction` for details + * about locators and log levels.
+ * + * @param tableLocator + * The locator to locate the table. + * @param text + * The text to verify. + * @param logLevel + * Default=INFO. Optional log level. + */ + @RobotKeyword + @ArgumentNames({ "tableLocator", "text", "logLevel=INFO" }) + public void tableShouldContain(String tableLocator, String text, String logLevel) { + WebElement element = TableElementFinder.findByContent(browserManagement.getCurrentWebDriver(), tableLocator, + text); if (element == null) { - logSource(loglevel); - throw new Selenium2LibraryNonFatalException( - String.format( - "Table identified by '%s' should have contained text '%s'.", - tableLocator, expected)); + logging.logSource(logLevel); + throw new Selenium2LibraryNonFatalException(String.format( + "Table identified by '%s' should have contained text '%s'.", tableLocator, text)); } } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Waiting.java b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Waiting.java index fe64f51..411b95a 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Waiting.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/keywords/Waiting.java @@ -1,83 +1,669 @@ package com.github.markusbernhardt.selenium2library.keywords; import org.openqa.selenium.JavascriptExecutor; +import org.robotframework.javalib.annotation.ArgumentNames; +import org.robotframework.javalib.annotation.Autowired; +import org.robotframework.javalib.annotation.RobotKeyword; +import org.robotframework.javalib.annotation.RobotKeywordOverload; +import org.robotframework.javalib.annotation.RobotKeywords; +import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; import com.github.markusbernhardt.selenium2library.utils.Robotframework; -public abstract class Waiting extends TableElement { +@RobotKeywords +public class Waiting extends RunOnFailureKeywordsAdapter { + + /** + * Instantiated BrowserManagement keyword bean + */ + @Autowired + protected BrowserManagement browserManagement; + + /** + * Instantiated Element keyword bean + */ + @Autowired + protected Element element; // ############################## // Keywords // ############################## + @RobotKeywordOverload public void waitForCondition(String condition) { waitForCondition(condition, null); } - public void waitForCondition(String condition, String timestr) { - waitForCondition(condition, timestr, null); + @RobotKeywordOverload + public void waitForCondition(String condition, String timeout) { + waitForCondition(condition, timeout, null); } - public void waitForCondition(final String condition, String timestr, - String error) { - if (error == null) { - error = String.format( - "Condition '%s' did not become true in ", - condition); + /** + * Waits until the given JavaScript condition is true.
+ *
+ * Fails, if the timeout expires, before the condition gets true.
+ *
+ * The condition may contain multiple JavaScript statements, but the last + * statement must return a boolean. Otherwise this keyword will always hit + * the timeout.
+ *
+ * Note that by default the code will be executed in the context of the + * Selenium object itself, so this will refer to the Selenium object. + * Use window to refer to the window of your application, e.g. + * window.document.getElementById('foo').
+ *
+ * See `Introduction` for details about timeouts.
+ * + * @param condition + * The JavaScript condition returning a boolean. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "condition", "timeout=NONE", "message=NONE" }) + public void waitForCondition(final String condition, String timeout, String message) { + if (message == null) { + message = String.format("Condition '%s' did not become true in ", condition); } - waitUntil(timestr, error, new WaitUntilFunction() { + waitUntil(timeout, message, new WaitUntilFunction() { @Override public boolean isFinished() { - return Boolean.TRUE.equals(((JavascriptExecutor) webDriverCache - .getCurrent()).executeScript(condition)); + return Boolean.TRUE.equals(((JavascriptExecutor) browserManagement.getCurrentWebDriver()) + .executeScript(condition)); } }); } + @RobotKeywordOverload public void waitUntilPageContains(String condition) { waitUntilPageContains(condition, null); } - public void waitUntilPageContains(String condition, String timestr) { - waitUntilPageContains(condition, timestr, null); + @RobotKeywordOverload + public void waitUntilPageContains(String condition, String timeout) { + waitUntilPageContains(condition, timeout, null); + } + + /** + * Waits until the current page contains text.
+ *
+ * Fails, if the timeout expires, before the text appears.
+ *
+ * See `Introduction` for details about timeouts.
+ * + * @param text + * The text to verify. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "condition", "timeout=NONE", "message=NONE" }) + public void waitUntilPageContains(final String text, String timeout, String message) { + if (message == null) { + message = String.format("Text '%s' did not appear in ", text); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + return element.isTextPresent(text); + } + }); + } + + @RobotKeywordOverload + public void waitUntilPageNotContains(String condition, String timeout) { + waitUntilPageNotContains(condition, timeout, null); } - public void waitUntilPageContains(final String text, String timestr, - String error) { - if (error == null) { - error = String - .format("Text '%s' did not appear in ", text); + @RobotKeywordOverload + public void waitUntilPageNotContains(String condition) { + waitUntilPageNotContains(condition, null); + } + + /** + * Waits until the current page does not contain text.
+ *
+ * Fails, if the timeout expires, before the text disappears.
+ *
+ * See `Introduction` for details about timeouts.
+ * + * @param text + * The text to verify. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "text", "timeout=NONE", "message=NONE" }) + public void waitUntilPageNotContains(final String text, String timeout, String message) { + if (message == null) { + message = String.format("Text '%s' did not disappear in ", text); } - waitUntil(timestr, error, new WaitUntilFunction() { + waitUntil(timeout, message, new WaitUntilFunction() { @Override public boolean isFinished() { - return isTextPresent(text); + return !element.isTextPresent(text); } }); } + @RobotKeywordOverload public void waitUntilPageContainsElement(String condition) { waitUntilPageContainsElement(condition, null); } - public void waitUntilPageContainsElement(String condition, String timestr) { - waitUntilPageContainsElement(condition, timestr, null); + @RobotKeywordOverload + public void waitUntilPageContainsElement(String condition, String timeout) { + waitUntilPageContainsElement(condition, timeout, null); + } + + /** + * Waits until the element identified by locator is found on the + * current page.
+ *
+ * Fails, if the timeout expires, before the element appears.
+ *
+ * See `Introduction` for details about locators and timeouts.
+ * + * @param locator + * The locator to locate the element. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "timeout=NONE", "message=NONE" }) + public void waitUntilPageContainsElement(final String locator, String timeout, String message) { + if (message == null) { + message = String.format("Element '%s' did not appear in ", locator); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + return element.isElementPresent(locator); + } + }); + } + + @RobotKeywordOverload + public void waitUntilPageNotContainsElement(String locator) { + waitUntilPageNotContainsElement(locator, null); + } + + @RobotKeywordOverload + public void waitUntilPageNotContainsElement(String locator, String timeout) { + waitUntilPageNotContainsElement(locator, timeout, null); + } + + /** + * Waits until the element identified by locator is not found on the + * current page.
+ *
+ * Fails, if the timeout expires, before the element disappears.
+ *
+ * See `Introduction` for details about locators and timeouts.
+ * + * @param locator + * The locator to locate the element. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "timeout=NONE", "message=NONE" }) + public void waitUntilPageNotContainsElement(final String locator, String timeout, String message) { + if (message == null) { + message = String.format("Element '%s' did not disappear in ", locator); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + return !element.isElementPresent(locator); + } + }); + } + + @RobotKeywordOverload + public void waitUntilElementIsVisible(String locator, String timeout) { + waitUntilElementIsVisible(locator, timeout, null); + } + + @RobotKeywordOverload + public void waitUntilElementIsVisible(String locator) { + waitUntilElementIsVisible(locator, null); + } + + /** + * Waits until the element identified by locator is visible.
+ *
+ * Fails, if the timeout expires, before the element gets visible.
+ *
+ * See `Introduction` for details about locators and timeouts.
+ * + * @param locator + * The locator to locate the element. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "timeout=NONE", "message=NONE" }) + public void waitUntilElementIsVisible(final String locator, String timeout, String message) { + if (message == null) { + message = String.format("Element '%s' not visible in ", locator); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + return element.isVisible(locator); + } + }); + } + + @RobotKeywordOverload + public void waitUntilElementIsNotVisible(String locator, String timeout) { + waitUntilElementIsNotVisible(locator, timeout, null); + } + + @RobotKeywordOverload + public void waitUntilElementIsNotVisible(String locator) { + waitUntilElementIsNotVisible(locator, null); + } + + /** + * Waits until the element identified by locator is not visible.
+ *
+ * Fails, if the timeout expires, before the element gets invisible.
+ *
+ * See `Introduction` for details about locators and timeouts.
+ * + * @param locator + * The locator to locate the element. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "timeout=NONE", "message=NONE" }) + public void waitUntilElementIsNotVisible(final String locator, String timeout, String message) { + if (message == null) { + message = String.format("Element '%s' still visible in ", locator); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + return !element.isVisible(locator); + } + }); + } + + @RobotKeywordOverload + public void waitUntilElementIsClickable(String locator) { + waitUntilElementIsClickable(locator, null, null); + } + + @RobotKeywordOverload + public void waitUntilElementIsClickable(String locator, String timeout) { + waitUntilElementIsClickable(locator, timeout, null); + } + + /** + * Waits until the element identified by locator is clickable.
+ *
+ * Fails, if the timeout expires, before the element gets clickable.
+ *
+ * See `Introduction` for details about locators and timeouts.
+ * + * @param locator + * The locator to locate the element. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "timeout=NONE", "message=NONE" }) + public void waitUntilElementIsClickable(final String locator, String timeout, String message) { + if (message == null) { + message = String.format("Element '%s' not clickable in ", locator); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + return element.isClickable(locator); + } + }); + } + + @RobotKeywordOverload + public void waitUntilElementIsNotClickable(String locator, String timeout) { + waitUntilElementIsNotClickable(locator, timeout, null); + } + + @RobotKeywordOverload + public void waitUntilElementIsNotClickable(String locator) { + waitUntilElementIsNotClickable(locator, null); + } + + /** + * Waits until the element identified by locator is not clickable.
+ *
+ * Fails, if the timeout expires, before the element gets unclickable.
+ *
+ * See `Introduction` for details about locators and timeouts.
+ * + * @param locator + * The locator to locate the element. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "timeout=NONE", "message=NONE" }) + public void waitUntilElementIsNotClickable(final String locator, String timeout, String message) { + if (message == null) { + message = String.format("Element '%s' still clickable in ", locator); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + return !element.isClickable(locator); + } + }); + } + + @RobotKeywordOverload + public void waitUntilElementIsSuccessfullyClicked(String locator, String timeout) { + waitUntilElementIsSuccessfullyClicked(locator, timeout, null); + } + + @RobotKeywordOverload + public void waitUntilElementIsSuccessfullyClicked(String locator) { + waitUntilElementIsSuccessfullyClicked(locator, null); + } + + /** + * Waits until the element identified by locator is sucessfully + * clicked on.
+ *
+ * Fails, if the timeout expires, before the element gets clicked on.
+ *
+ * See `Introduction` for details about locators and timeouts.
+ * + * @param locator + * The locator to locate the element. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "timeout=NONE", "message=NONE" }) + public void waitUntilElementIsSuccessfullyClicked(final String locator, String timeout, String message) { + if (message == null) { + message = String.format("Element '%s' not successfully clicked in ", locator); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + element.clickElement(locator); + return true; + } + }); + } + + @RobotKeywordOverload + public void waitUntilElementIsSelected(String locator, String timeout) { + waitUntilElementIsSelected(locator, timeout, null); + } + + @RobotKeywordOverload + public void waitUntilElementIsSelected(String locator) { + waitUntilElementIsSelected(locator, null); + } + + /** + * Waits until the element identified by locator is selected.
+ *
+ * Fails, if the timeout expires, before the element gets selected.
+ *
+ * See `Introduction` for details about locators and timeouts.
+ * + * @param locator + * The locator to locate the element. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "timeout=NONE", "message=NONE" }) + public void waitUntilElementIsSelected(final String locator, String timeout, String message) { + if (message == null) { + message = String.format("Element '%s' not selected in ", locator); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + return element.isSelected(locator); + } + }); + } + + @RobotKeywordOverload + public void waitUntilElementIsNotSelected(String locator, String timeout) { + waitUntilElementIsNotSelected(locator, timeout, null); + } + + @RobotKeywordOverload + public void waitUntilElementIsNotSelected(String locator) { + waitUntilElementIsNotSelected(locator, null); + } + + /** + * Waits until the element identified by locator is not selected.
+ *
+ * Fails, if the timeout expires, before the element gets unselected.
+ *
+ * See `Introduction` for details about locators and timeouts.
+ * + * @param locator + * The locator to locate the element. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "locator", "timeout=NONE", "message=NONE" }) + public void waitUntilElementIsNotSelected(final String locator, String timeout, String message) { + if (message == null) { + message = String.format("Element '%s' still selected in ", locator); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + return !element.isSelected(locator); + } + }); + } + + @RobotKeywordOverload + public void waitUntilTitleContains(String title, String timeout) { + waitUntilTitleContains(title, timeout, null); + } + + @RobotKeywordOverload + public void waitUntilTitleContains(String title) { + waitUntilTitleContains(title, null, null); + } + + /** + * Waits until the current page title contains title.
+ *
+ * Fails, if the timeout expires, before the page title contains the given + * title.
+ * + * @param title + * The title to verify. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "title", "timeout=NONE", "message=NONE" }) + public void waitUntilTitleContains(final String title, String timeout, String message) { + if (message == null) { + message = String.format("Title '%s' did not appear in ", title); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + String currentTitle = browserManagement.getTitle(); + return currentTitle != null && currentTitle.contains(title); + } + }); + } + + @RobotKeywordOverload + public void waitUntilTitleNotContains(String title, String timeout) { + waitUntilTitleNotContains(title, timeout, null); + } + + @RobotKeywordOverload + public void waitUntilTitleNotContains(String title) { + waitUntilTitleNotContains(title, null, null); + } + + /** + * Waits until the current page title does not contain title.
+ *
+ * Fails, if the timeout expires, before the page title does not contain the + * given title.
+ * + * @param title + * The title to verify. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "title", "timeout=NONE", "message=NONE" }) + public void waitUntilTitleNotContains(final String title, String timeout, String message) { + if (message == null) { + message = String.format("Title '%s' did not appear in ", title); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + String currentTitle = browserManagement.getTitle(); + return currentTitle == null || !currentTitle.contains(title); + } + }); + } + + @RobotKeywordOverload + public void waitUntilTitleIs(String title, String timeout) { + waitUntilTitleIs(title, timeout, null); + } + + @RobotKeywordOverload + public void waitUntilTitleIs(String title) { + waitUntilTitleIs(title, null); + } + + /** + * Waits until the current page title is exactly title.
+ *
+ * Fails, if the timeout expires, before the page title matches the given + * title.
+ * + * @param title + * The title to verify. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "title", "timeout=NONE", "message=NONE" }) + public void waitUntilTitleIs(final String title, String timeout, String message) { + if (message == null) { + message = String.format("Title '%s' did not appear in ", title); + } + waitUntil(timeout, message, new WaitUntilFunction() { + + @Override + public boolean isFinished() { + String currentTitle = browserManagement.getTitle(); + return currentTitle != null && currentTitle.equals(title); + } + }); + } + + @RobotKeywordOverload + public void waitUntilTitleIsNot(String title, String timeout) { + waitUntilTitleIsNot(title, timeout, null); + } + + @RobotKeywordOverload + public void waitUntilTitleIsNot(String title) { + waitUntilTitleIsNot(title, null, null); } - public void waitUntilPageContainsElement(final String locator, - String timestr, String error) { - if (error == null) { - error = String.format("Element '%s' did not appear in ", - locator); + /** + * Waits until the current page title is not exactly title.
+ *
+ * Fails, if the timeout expires, before the page title does not match the + * given title.
+ * + * @param title + * The title to verify. + * @param timeout + * Default=NONE. Optional timeout interval. + * @param message + * Default=NONE. Optional custom error message. + */ + @RobotKeyword + @ArgumentNames({ "title", "timeout=NONE", "message=NONE" }) + public void waitUntilTitleIsNot(final String title, String timeout, String message) { + if (message == null) { + message = String.format("Title '%s' did not appear in ", title); } - waitUntil(timestr, error, new WaitUntilFunction() { + waitUntil(timeout, message, new WaitUntilFunction() { @Override public boolean isFinished() { - return isElementPresent(locator); + String currentTitle = browserManagement.getTitle(); + return currentTitle == null || !currentTitle.equals(title); } }); } @@ -86,12 +672,9 @@ public boolean isFinished() { // Internal Methods // ############################## - protected void waitUntil(String timestr, String error, - WaitUntilFunction function) { - double timeout = timestr != null ? Robotframework - .timestrToSecs(timestr) : this.timeout; - error = error.replace("", - Robotframework.secsToTimestr(timeout)); + protected void waitUntil(String timestr, String message, WaitUntilFunction function) { + double timeout = timestr != null ? Robotframework.timestrToSecs(timestr) : browserManagement.getTimeout(); + message = message.replace("", Robotframework.secsToTimestr(timeout)); long maxtime = System.currentTimeMillis() + (long) (timeout * 1000); for (;;) { try { @@ -101,7 +684,7 @@ protected void waitUntil(String timestr, String error, } catch (Throwable t) { } if (System.currentTimeMillis() > maxtime) { - throw new Selenium2LibraryNonFatalException(error); + throw new Selenium2LibraryNonFatalException(message); } try { Thread.sleep(200); diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/locators/ElementFinder.java b/src/main/java/com/github/markusbernhardt/selenium2library/locators/ElementFinder.java index 56c4cf4..c82b320 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/locators/ElementFinder.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/locators/ElementFinder.java @@ -14,21 +14,20 @@ import org.openqa.selenium.WebElement; import org.python.util.PythonInterpreter; -import com.github.markusbernhardt.selenium2library.Selenium2Library; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; +import com.github.markusbernhardt.selenium2library.keywords.Element; import com.github.markusbernhardt.selenium2library.utils.Python; public class ElementFinder { - private final static Hashtable registeredLocationStrategies = new Hashtable(); + protected final static Hashtable registeredLocationStrategies = new Hashtable(); - private enum KeyAttrs { - DEFAULT("@id,@name"), A( - "@id,@name,@href,normalize-space(descendant-or-self::text())"), IMG( + protected enum KeyAttrs { + DEFAULT("@id,@name"), A("@id,@name,@href,normalize-space(descendant-or-self::text())"), IMG( "@id,@name,@src,@alt"), INPUT("@id,@name,@value,@src"), BUTTON( "@id,@name,@value,normalize-space(descendant-or-self::text())"); - private String[] keyAttrs; + protected String[] keyAttrs; KeyAttrs(String keyAttrs) { this.keyAttrs = keyAttrs.split(","); @@ -39,18 +38,16 @@ public String[] getKeyAttrs() { } } - private interface Strategy { - List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates); + protected interface Strategy { + List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates); }; - private enum StrategyEnum implements Strategy { + protected enum StrategyEnum implements Strategy { DEFAULT { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { if (findByCoordinates.criteria.startsWith("//")) { return XPATH.findBy(webDriver, findByCoordinates); } @@ -60,88 +57,69 @@ public List findBy(WebDriver webDriver, IDENTIFIER { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { - List elements = webDriver.findElements(By - .id(findByCoordinates.criteria)); - elements.addAll(webDriver.findElements(By - .name(findByCoordinates.criteria))); + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { + List elements = webDriver.findElements(By.id(findByCoordinates.criteria)); + elements.addAll(webDriver.findElements(By.name(findByCoordinates.criteria))); return filterElements(elements, findByCoordinates); } }, ID { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { - return filterElements(webDriver.findElements(By - .id(findByCoordinates.criteria)), findByCoordinates); + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { + return filterElements(webDriver.findElements(By.id(findByCoordinates.criteria)), findByCoordinates); } }, NAME { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { - return filterElements(webDriver.findElements(By - .name(findByCoordinates.criteria)), findByCoordinates); + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { + return filterElements(webDriver.findElements(By.name(findByCoordinates.criteria)), findByCoordinates); } }, XPATH { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { - return filterElements(webDriver.findElements(By - .xpath(findByCoordinates.criteria)), findByCoordinates); + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { + return filterElements(webDriver.findElements(By.xpath(findByCoordinates.criteria)), findByCoordinates); } }, DOM { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { - Object result = ((JavascriptExecutor) webDriver) - .executeScript(String.format("return %s;", - findByCoordinates.criteria)); + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { + Object result = ((JavascriptExecutor) webDriver).executeScript(String.format("return %s;", + findByCoordinates.criteria)); return filterElements(toList(result), findByCoordinates); } }, LINK { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { - return filterElements(webDriver.findElements(By - .linkText(findByCoordinates.criteria)), + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { + return filterElements(webDriver.findElements(By.linkText(findByCoordinates.criteria)), findByCoordinates); } }, CSS { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { - return filterElements(webDriver.findElements(By - .cssSelector(findByCoordinates.criteria)), + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { + return filterElements(webDriver.findElements(By.cssSelector(findByCoordinates.criteria)), findByCoordinates); } }, TAG { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { - return filterElements(webDriver.findElements(By - .tagName(findByCoordinates.criteria)), - findByCoordinates); + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { + return filterElements(webDriver.findElements(By.tagName(findByCoordinates.criteria)), findByCoordinates); } }, JQUERY { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { return findByJQuerySizzle(webDriver, findByCoordinates); } @@ -150,8 +128,7 @@ public List findBy(WebDriver webDriver, SIZZLE { @Override - public List findBy(WebDriver webDriver, - FindByCoordinates findByCoordinates) { + public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { return findByJQuerySizzle(webDriver, findByCoordinates); } @@ -160,18 +137,15 @@ public List findBy(WebDriver webDriver, } - private static List findByJQuerySizzle(WebDriver webDriver, - FindByCoordinates findByCoordinates) { - String js = String.format("return jQuery('%s').get();", - findByCoordinates.criteria.replace("'", "\\'")); + protected static List findByJQuerySizzle(WebDriver webDriver, FindByCoordinates findByCoordinates) { + String js = String.format("return jQuery('%s').get();", findByCoordinates.criteria.replace("'", "\\'")); Object o = ((JavascriptExecutor) webDriver).executeScript(js); List list = toList(o); return filterElements(list, findByCoordinates); } - private static List filterElements(List elements, - FindByCoordinates findByCoordinates) { + protected static List filterElements(List elements, FindByCoordinates findByCoordinates) { if (findByCoordinates.tag == null) { return elements; } @@ -185,16 +159,14 @@ private static List filterElements(List elements, return result; } - private static boolean elementMatches(WebElement element, - FindByCoordinates findByCoordinates) { + protected static boolean elementMatches(WebElement element, FindByCoordinates findByCoordinates) { if (!element.getTagName().toLowerCase().equals(findByCoordinates.tag)) { return false; } if (findByCoordinates.constraints != null) { for (String name : findByCoordinates.constraints.keySet()) { - if (!element.getAttribute(name).equals( - findByCoordinates.constraints.get(name))) { + if (!element.getAttribute(name).equals(findByCoordinates.constraints.get(name))) { return false; } } @@ -203,49 +175,38 @@ private static boolean elementMatches(WebElement element, return true; } - private static List findByKeyAttrs(WebDriver webDriver, - FindByCoordinates findByCoordinates) { + protected static List findByKeyAttrs(WebDriver webDriver, FindByCoordinates findByCoordinates) { KeyAttrs keyAttrs = KeyAttrs.DEFAULT; if (findByCoordinates.tag != null) { try { - keyAttrs = KeyAttrs.valueOf(findByCoordinates.tag.trim() - .toUpperCase()); + keyAttrs = KeyAttrs.valueOf(findByCoordinates.tag.trim().toUpperCase()); } catch (IllegalArgumentException e) { // No special keyAttrs available for this tag } } - String xpathCriteria = Selenium2Library - .escapeXpathValue(findByCoordinates.criteria); + String xpathCriteria = Element.escapeXpathValue(findByCoordinates.criteria); String xpathTag = findByCoordinates.tag; if (findByCoordinates.tag == null) { xpathTag = "*"; } List xpathConstraints = new ArrayList(); if (findByCoordinates.constraints != null) { - for (Entry entry : findByCoordinates.constraints - .entrySet()) { - xpathConstraints.add(String.format("@%s='%s'", entry.getKey(), - entry.getValue())); + for (Entry entry : findByCoordinates.constraints.entrySet()) { + xpathConstraints.add(String.format("@%s='%s'", entry.getKey(), entry.getValue())); } } List xpathSearchers = new ArrayList(); for (String attr : keyAttrs.getKeyAttrs()) { xpathSearchers.add(String.format("%s=%s", attr, xpathCriteria)); } - xpathSearchers.addAll(getAttrsWithUrl(webDriver, keyAttrs, - findByCoordinates.criteria)); - String xpath = String.format( - "//%s[%s(%s)]", - xpathTag, - Python.join(" and ", xpathConstraints) - + (xpathConstraints.size() > 0 ? " and " : ""), - Python.join(" or ", xpathSearchers)); + xpathSearchers.addAll(getAttrsWithUrl(webDriver, keyAttrs, findByCoordinates.criteria)); + String xpath = String.format("//%s[%s(%s)]", xpathTag, Python.join(" and ", xpathConstraints) + + (xpathConstraints.size() > 0 ? " and " : ""), Python.join(" or ", xpathSearchers)); return webDriver.findElements(By.xpath(xpath)); } - private static List getAttrsWithUrl(WebDriver webDriver, - KeyAttrs keyAttrs, String criteria) { + protected static List getAttrsWithUrl(WebDriver webDriver, KeyAttrs keyAttrs, String criteria) { List attrs = new ArrayList(); String url = null; String xpathUrl = null; @@ -255,7 +216,7 @@ private static List getAttrsWithUrl(WebDriver webDriver, if (attr.equals(keyAttr)) { if (url == null || xpathUrl == null) { url = getBaseUrl(webDriver) + "/" + criteria; - xpathUrl = Selenium2Library.escapeXpathValue(url); + xpathUrl = Element.escapeXpathValue(url); } attrs.add(String.format("%s=%s", attr, xpathUrl)); } @@ -264,7 +225,7 @@ private static List getAttrsWithUrl(WebDriver webDriver, return attrs; } - private static String getBaseUrl(WebDriver webDriver) { + protected static String getBaseUrl(WebDriver webDriver) { String url = webDriver.getCurrentUrl(); int lastIndex = url.lastIndexOf('/'); if (lastIndex != -1) { @@ -273,25 +234,20 @@ private static String getBaseUrl(WebDriver webDriver) { return url; } - public static void addLocationStrategy(String strategyName, - String functionDefinition, String delimiter) { - registeredLocationStrategies.put(strategyName.toUpperCase(), - new CustomStrategy(functionDefinition, delimiter)); + public static void addLocationStrategy(String strategyName, String functionDefinition, String delimiter) { + registeredLocationStrategies.put(strategyName.toUpperCase(), new CustomStrategy(functionDefinition, delimiter)); } public static List find(WebDriver webDriver, String locator) { return find(webDriver, locator, null); } - public static List find(WebDriver webDriver, String locator, - String tag) { + public static List find(WebDriver webDriver, String locator, String tag) { if (webDriver == null) { - throw new Selenium2LibraryNonFatalException( - "ElementFinder.find: webDriver is null."); + throw new Selenium2LibraryNonFatalException("ElementFinder.find: webDriver is null."); } if (locator == null) { - throw new Selenium2LibraryNonFatalException( - "ElementFinder.find: locator is null."); + throw new Selenium2LibraryNonFatalException("ElementFinder.find: locator is null."); } FindByCoordinates findByCoordinates = new FindByCoordinates(); @@ -305,20 +261,17 @@ public static List find(WebDriver webDriver, String locator, @Override protected PythonInterpreter initialValue() { PythonInterpreter pythonInterpreter = new PythonInterpreter(); - pythonInterpreter - .exec("from robot.variables import GLOBAL_VARIABLES; from robot.api import logger;"); + pythonInterpreter.exec("from robot.variables import GLOBAL_VARIABLES; from robot.api import logger;"); return pythonInterpreter; } }; protected static void warn(String msg) { loggingPythonInterpreter.get().exec( - String.format("logger.warn('%s');", msg.replace("'", "\\'") - .replace("\n", "\\n"))); + String.format("logger.warn('%s');", msg.replace("'", "\\'").replace("\n", "\\n"))); } - private static Strategy parseLocator(FindByCoordinates findByCoordinates, - String locator) { + protected static Strategy parseLocator(FindByCoordinates findByCoordinates, String locator) { String prefix = null; String criteria = locator; if (!locator.startsWith("//")) { @@ -335,8 +288,7 @@ private static Strategy parseLocator(FindByCoordinates findByCoordinates, strategy = StrategyEnum.valueOf(prefix); } catch (IllegalArgumentException e) { // No standard locator type. Look for custom strategy - CustomStrategy customStrategy = registeredLocationStrategies - .get(prefix); + CustomStrategy customStrategy = registeredLocationStrategies.get(prefix); if (customStrategy != null) { strategy = customStrategy; } @@ -346,8 +298,7 @@ private static Strategy parseLocator(FindByCoordinates findByCoordinates, return strategy; } - private static void parseTag(FindByCoordinates findByCoordinates, - Strategy strategy, String tag) { + protected static void parseTag(FindByCoordinates findByCoordinates, Strategy strategy, String tag) { if (tag == null) { return; } @@ -359,6 +310,8 @@ private static void parseTag(FindByCoordinates findByCoordinates, tag = "img"; } else if (tag.equals("list")) { tag = "select"; + } else if (tag.equals("text area")) { + tag = "textarea"; } else if (tag.equals("radio button")) { tag = "input"; constraints.put("type", "radio"); @@ -377,7 +330,7 @@ private static void parseTag(FindByCoordinates findByCoordinates, } @SuppressWarnings("unchecked") - private static List toList(Object o) { + protected static List toList(Object o) { if (o instanceof List) { return (List) o; } @@ -389,18 +342,18 @@ private static List toList(Object o) { return list; } - private static class FindByCoordinates { + protected static class FindByCoordinates { String criteria; String tag; Map constraints; } - private static class CustomStrategy implements Strategy { + protected static class CustomStrategy implements Strategy { - private String functionDefinition; + protected String functionDefinition; - private String delimiter; + protected String delimiter; public CustomStrategy(String functionDefinition, String delimiter) { this.functionDefinition = functionDefinition; @@ -408,8 +361,7 @@ public CustomStrategy(String functionDefinition, String delimiter) { } @Override - public List findBy(final WebDriver webDriver, - final FindByCoordinates findByCoordinates) { + public List findBy(final WebDriver webDriver, final FindByCoordinates findByCoordinates) { return filterElements(webDriver.findElements(new By() { @Override @@ -419,15 +371,13 @@ public List findElements(SearchContext context) { arguments = new Object[1]; arguments[0] = findByCoordinates.criteria; } else { - String[] splittedCriteria = findByCoordinates.criteria - .split(delimiter); + String[] splittedCriteria = findByCoordinates.criteria.split(delimiter); arguments = new Object[splittedCriteria.length]; for (int i = 0; i < splittedCriteria.length; i++) { arguments[i] = splittedCriteria[i]; } } - Object o = ((JavascriptExecutor) webDriver).executeScript( - functionDefinition, arguments); + Object o = ((JavascriptExecutor) webDriver).executeScript(functionDefinition, arguments); return toList(o); } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/locators/TableElementFinder.java b/src/main/java/com/github/markusbernhardt/selenium2library/locators/TableElementFinder.java index 0fe738a..a0410fc 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/locators/TableElementFinder.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/locators/TableElementFinder.java @@ -10,7 +10,7 @@ public class TableElementFinder { - private final static TreeMap> locatorSuffixesMap; + protected final static TreeMap> locatorSuffixesMap; static { locatorSuffixesMap = new TreeMap>(); @@ -19,24 +19,21 @@ public class TableElementFinder { addLocatorSuffix(locatorSuffixesMap, "css.header", " th"); addLocatorSuffix(locatorSuffixesMap, "css.footer", " tfoot td"); addLocatorSuffix(locatorSuffixesMap, "css.row", " tr:nth-child(%s)"); - addLocatorSuffix(locatorSuffixesMap, "css.col", " tr td:nth-child(%s)", - " tr th:nth-child(%s)"); + addLocatorSuffix(locatorSuffixesMap, "css.col", " tr td:nth-child(%s)", " tr th:nth-child(%s)"); addLocatorSuffix(locatorSuffixesMap, "sizzle.default", ""); addLocatorSuffix(locatorSuffixesMap, "sizzle.content", ""); addLocatorSuffix(locatorSuffixesMap, "sizzle.header", " th"); addLocatorSuffix(locatorSuffixesMap, "sizzle.footer", " tfoot td"); addLocatorSuffix(locatorSuffixesMap, "sizzle.row", " tr:nth-child(%s)"); - addLocatorSuffix(locatorSuffixesMap, "sizzle.col", - " tr td:nth-child(%s)", " tr th:nth-child(%s)"); + addLocatorSuffix(locatorSuffixesMap, "sizzle.col", " tr td:nth-child(%s)", " tr th:nth-child(%s)"); addLocatorSuffix(locatorSuffixesMap, "xpath.default", ""); addLocatorSuffix(locatorSuffixesMap, "xpath.content", "//*"); addLocatorSuffix(locatorSuffixesMap, "xpath.header", "//th"); addLocatorSuffix(locatorSuffixesMap, "xpath.footer", "//tfoot//td"); addLocatorSuffix(locatorSuffixesMap, "xpath.row", "//tr[%s]//*"); - addLocatorSuffix(locatorSuffixesMap, "xpath.col", - "//tr//*[self::td or self::th][%s]"); + addLocatorSuffix(locatorSuffixesMap, "xpath.col", "//tr//*[self::td or self::th][%s]"); } public static WebElement find(WebDriver webDriver, String tableLocator) { @@ -44,49 +41,40 @@ public static WebElement find(WebDriver webDriver, String tableLocator) { return searchInLocators(webDriver, locators, null); } - public static WebElement findByContent(WebDriver webDriver, - String tableLocator, String content) { + public static WebElement findByContent(WebDriver webDriver, String tableLocator, String content) { List locators = parseTableLocator(tableLocator, "content"); return searchInLocators(webDriver, locators, content); } - public static WebElement findByHeader(WebDriver webDriver, - String tableLocator, String content) { + public static WebElement findByHeader(WebDriver webDriver, String tableLocator, String content) { List locators = parseTableLocator(tableLocator, "header"); return searchInLocators(webDriver, locators, content); } - public static WebElement findByFooter(WebDriver webDriver, - String tableLocator, String content) { + public static WebElement findByFooter(WebDriver webDriver, String tableLocator, String content) { List locators = parseTableLocator(tableLocator, "footer"); return searchInLocators(webDriver, locators, content); } - public static WebElement findByRow(WebDriver webDriver, - String tableLocator, int row, String content) { + public static WebElement findByRow(WebDriver webDriver, String tableLocator, int row, String content) { List locators = parseTableLocator(tableLocator, "row"); List formattedLocators = new ArrayList(); for (String locator : locators) { - formattedLocators - .add(String.format(locator, Integer.toString(row))); + formattedLocators.add(String.format(locator, Integer.toString(row))); } return searchInLocators(webDriver, formattedLocators, content); } - public static WebElement findByCol(WebDriver webDriver, - String tableLocator, int col, String content) { + public static WebElement findByCol(WebDriver webDriver, String tableLocator, int col, String content) { List locators = parseTableLocator(tableLocator, "col"); List formattedLocators = new ArrayList(); for (String locator : locators) { - formattedLocators - .add(String.format(locator, Integer.toString(col))); + formattedLocators.add(String.format(locator, Integer.toString(col))); } return searchInLocators(webDriver, formattedLocators, content); } - private static void addLocatorSuffix( - Map> locatorSuffixesMap, String key, - String... values) { + protected static void addLocatorSuffix(Map> locatorSuffixesMap, String key, String... values) { List list = new ArrayList(); for (String value : values) { list.add(value); @@ -94,14 +82,12 @@ private static void addLocatorSuffix( locatorSuffixesMap.put(key, list); } - private static List parseTableLocator(String tableLocator, - String locationMethod) { + protected static List parseTableLocator(String tableLocator, String locationMethod) { String tableLocatorType = null; if (tableLocator.startsWith("xpath=")) { tableLocatorType = "xpath."; - } else if (tableLocator.startsWith("jquery=") - || tableLocator.startsWith("sizzle=")) { + } else if (tableLocator.startsWith("jquery=") || tableLocator.startsWith("sizzle=")) { tableLocatorType = "sizzle."; } else { if (!tableLocator.startsWith("css=")) { @@ -110,8 +96,7 @@ private static List parseTableLocator(String tableLocator, tableLocatorType = "css."; } - List locatorSuffixes = locatorSuffixesMap.get(tableLocatorType - + locationMethod); + List locatorSuffixes = locatorSuffixesMap.get(tableLocatorType + locationMethod); List parsedTabeLocators = new ArrayList(); for (String locatorSuffix : locatorSuffixes) { @@ -120,8 +105,7 @@ private static List parseTableLocator(String tableLocator, return parsedTabeLocators; } - private static WebElement searchInLocators(WebDriver webDriver, - List locators, String content) { + protected static WebElement searchInLocators(WebDriver webDriver, List locators, String content) { for (String locator : locators) { List elements = ElementFinder.find(webDriver, locator); for (WebElement element : elements) { diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/locators/WindowManager.java b/src/main/java/com/github/markusbernhardt/selenium2library/locators/WindowManager.java index e623da5..8f28909 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/locators/WindowManager.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/locators/WindowManager.java @@ -4,6 +4,7 @@ import java.util.List; import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.NoSuchWindowException; import org.openqa.selenium.WebDriver; import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; @@ -15,15 +16,12 @@ public class WindowManager { public static int WINDOW_INFO_INDEX_DOCUMENT_TITLE = 2; public static int WINDOW_INFO_INDEX_DOCUMENT_URL = 3; - private enum WindowManagerStrategy { + protected enum WindowManagerStrategy { DEFAULT { @Override - public void select(WebDriver webDriver, - SelectCoordinates selectCoordinates) { - if (selectCoordinates.criteria == null - || selectCoordinates.criteria.toLowerCase().equals( - "null")) { + public void select(WebDriver webDriver, SelectCoordinates selectCoordinates) { + if (selectCoordinates.criteria == null || selectCoordinates.criteria.toLowerCase().equals("null")) { webDriver.switchTo().window(""); return; } @@ -39,88 +37,76 @@ public void select(WebDriver webDriver, return; } catch (Throwable t) { } - throw new Selenium2LibraryNonFatalException( - "Unable to locate window with name or title '" - + selectCoordinates.criteria + "'"); + throw new Selenium2LibraryNonFatalException("Unable to locate window with name or title '" + + selectCoordinates.criteria + "'"); } }, TITLE { @Override - public void select(WebDriver webDriver, - final SelectCoordinates selectCoordinates) { + public void select(WebDriver webDriver, final SelectCoordinates selectCoordinates) { selectMatching(webDriver, new Matcher() { @Override public boolean match(List currentWindowInfo) { - return currentWindowInfo - .get(WINDOW_INFO_INDEX_DOCUMENT_TITLE) - .trim() - .toLowerCase() - .equals(selectCoordinates.criteria - .toLowerCase()); + return currentWindowInfo.get(WINDOW_INFO_INDEX_DOCUMENT_TITLE).trim().toLowerCase() + .equals(selectCoordinates.criteria.toLowerCase()); } - }, "Unable to locate window with title '" - + selectCoordinates.criteria + "'"); + }, "Unable to locate window with title '" + selectCoordinates.criteria + "'"); } }, NAME { @Override - public void select(WebDriver webDriver, - final SelectCoordinates selectCoordinates) { + public void select(WebDriver webDriver, final SelectCoordinates selectCoordinates) { selectMatching(webDriver, new Matcher() { @Override public boolean match(List currentWindowInfo) { - return currentWindowInfo - .get(WINDOW_INFO_INDEX_WINDOW_NAME) - .trim() - .toLowerCase() - .equals(selectCoordinates.criteria - .toLowerCase()); + return currentWindowInfo.get(WINDOW_INFO_INDEX_WINDOW_NAME).trim().toLowerCase() + .equals(selectCoordinates.criteria.toLowerCase()); } - }, "Unable to locate window with name '" - + selectCoordinates.criteria + "'"); + }, "Unable to locate window with name '" + selectCoordinates.criteria + "'"); } }, URL { @Override - public void select(WebDriver webDriver, - final SelectCoordinates selectCoordinates) { + public void select(WebDriver webDriver, final SelectCoordinates selectCoordinates) { selectMatching(webDriver, new Matcher() { @Override public boolean match(List currentWindowInfo) { - return currentWindowInfo - .get(WINDOW_INFO_INDEX_DOCUMENT_URL) - .trim() - .toLowerCase() - .equals(selectCoordinates.criteria - .toLowerCase()); + return currentWindowInfo.get(WINDOW_INFO_INDEX_DOCUMENT_URL).trim().toLowerCase() + .equals(selectCoordinates.criteria.toLowerCase()); } - }, "Unable to locate window with URL '" - + selectCoordinates.criteria + "'"); + }, "Unable to locate window with URL '" + selectCoordinates.criteria + "'"); } }; - abstract public void select(WebDriver webDriver, - SelectCoordinates selectCoordinates); + abstract public void select(WebDriver webDriver, SelectCoordinates selectCoordinates); + + protected static void selectMatching(WebDriver webDriver, Matcher matcher, String error) { + String startingHandle = null; + try { + startingHandle = webDriver.getWindowHandle(); + } catch (NoSuchWindowException e) { + // Window of current WebDriver instance is already closed + } - protected static void selectMatching(WebDriver webDriver, - Matcher matcher, String error) { - String startingHandle = webDriver.getWindowHandle(); for (String handle : webDriver.getWindowHandles()) { webDriver.switchTo().window(handle); if (matcher.match(getCurrentWindowInfo(webDriver))) { return; } } - webDriver.switchTo().window(startingHandle); + + if (startingHandle != null) { + webDriver.switchTo().window(startingHandle); + } throw new Selenium2LibraryNonFatalException(error); } } @@ -150,38 +136,43 @@ public static List getWindowTitles(WebDriver webDriver) { } public static List> getWindowInfos(WebDriver webDriver) { + String startingHandle = null; + try { + startingHandle = webDriver.getWindowHandle(); + } catch (NoSuchWindowException e) { + // Window of current WebDriver instance is already closed + } + List> windowInfos = new ArrayList>(); - String startingHandle = webDriver.getWindowHandle(); try { for (String handle : webDriver.getWindowHandles()) { webDriver.switchTo().window(handle); windowInfos.add(getCurrentWindowInfo(webDriver)); } } finally { - webDriver.switchTo().window(startingHandle); + if (startingHandle != null) { + webDriver.switchTo().window(startingHandle); + } } return windowInfos; } public static void select(WebDriver webDriver, String locator) { if (webDriver == null) { - throw new Selenium2LibraryNonFatalException( - "WindowManager.select: webDriver is null."); + throw new Selenium2LibraryNonFatalException("WindowManager.select: webDriver is null."); } SelectCoordinates selectCoordinates = new SelectCoordinates(); - WindowManagerStrategy strategy = parseLocator(selectCoordinates, - locator); + WindowManagerStrategy strategy = parseLocator(selectCoordinates, locator); strategy.select(webDriver, selectCoordinates); } - protected static WindowManagerStrategy parseLocator( - SelectCoordinates selectCoordinates, String locator) { + protected static WindowManagerStrategy parseLocator(SelectCoordinates selectCoordinates, String locator) { String prefix = null; String criteria = locator; if (locator != null && locator.length() > 0) { String[] locatorParts = locator.split("=", 2); - if (locatorParts[1] != null) { + if (locatorParts.length == 2) { prefix = locatorParts[0].trim().toUpperCase(); criteria = locatorParts[1].trim(); } diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/utils/Javadoc2Libdoc.java b/src/main/java/com/github/markusbernhardt/selenium2library/utils/Javadoc2Libdoc.java new file mode 100644 index 0000000..c550275 --- /dev/null +++ b/src/main/java/com/github/markusbernhardt/selenium2library/utils/Javadoc2Libdoc.java @@ -0,0 +1,175 @@ +package com.github.markusbernhardt.selenium2library.utils; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import com.github.markusbernhardt.xmldoclet.xjc.AnnotationInstance; +import com.github.markusbernhardt.xmldoclet.xjc.Class; +import com.github.markusbernhardt.xmldoclet.xjc.Constructor; +import com.github.markusbernhardt.xmldoclet.xjc.Method; +import com.github.markusbernhardt.xmldoclet.xjc.ObjectFactory; +import com.github.markusbernhardt.xmldoclet.xjc.Package; +import com.github.markusbernhardt.xmldoclet.xjc.Root; +import com.github.markusbernhardt.xmldoclet.xjc.TagInfo; + +public class Javadoc2Libdoc { + protected final Map keywordDocumentationMap; + + public Javadoc2Libdoc(java.lang.Class clazz) { + InputStream inputStream = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(clazz.getName().replace('.', '/') + ".javadoc"); + Root root = loadJavadocRoot(inputStream); + keywordDocumentationMap = loadKeywordDocumentationMap(root, clazz.getName()); + } + + public String getKeywordDocumentation(String keywordName) { + return keywordDocumentationMap.get(keywordName); + } + + protected Root loadJavadocRoot(InputStream inputStream) { + try { + JAXBContext context = JAXBContext.newInstance(Root.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + return (Root) unmarshaller.unmarshal(inputStream); + } catch (JAXBException e) { + return new ObjectFactory().createRoot(); + } + } + + protected Map loadKeywordDocumentationMap(Root root, String className) { + Map keywordDocumentation = new HashMap(); + for (Package packageNode : root.getPackage()) { + for (Class classNode : packageNode.getClazz()) { + if (className.equals(classNode.getQualified())) { + keywordDocumentation.put("__intro__", formatComment(classNode)); + Constructor constructorNodeWithComment = null; + for (Constructor constructorNode : classNode.getConstructor()) { + if (constructorNode.getComment() != null && constructorNode.getComment().trim().length() > 0) { + constructorNodeWithComment = constructorNode; + } + } + keywordDocumentation.put("__init__", formatComment(constructorNodeWithComment)); + } + for (Method methodNode : classNode.getMethod()) { + for (AnnotationInstance annotationInstanceNode : methodNode.getAnnotation()) { + if (annotationInstanceNode.getName().equals("RobotKeyword")) { + keywordDocumentation.put(methodNode.getName(), formatComment(methodNode)); + break; + } + } + } + } + } + return keywordDocumentation; + } + + protected String formatComment(Class classNode) { + if (classNode.getComment() != null) { + return classNode.getComment(); + } + return ""; + } + + protected String formatComment(Constructor constructorNode) { + if (constructorNode.getComment() != null) { + return constructorNode.getComment(); + } + return ""; + } + + protected String formatComment(Method methodNode) { + StringBuilder stringBuilder = new StringBuilder(); + + if (methodNode.getComment() != null) { + stringBuilder.append(methodNode.getComment()); + } + + stringBuilder.append(formatParam(methodNode)); + stringBuilder.append(formatReturn(methodNode)); + stringBuilder.append(formatSee(methodNode)); + + return stringBuilder.toString(); + } + + protected String formatParam(Method methodNode) { + boolean hasTag = false; + StringBuilder stringBuilderParam = new StringBuilder(); + stringBuilderParam.append("
Parameters:
"); + for (TagInfo tagInfo : methodNode.getTag()) { + if (!tagInfo.getName().equals("@param")) { + continue; + } + hasTag = true; + String text = tagInfo.getText(); + int index = text.indexOf('\n'); + stringBuilderParam.append("    "); + stringBuilderParam.append(text.substring(0, index)); + stringBuilderParam.append(" "); + stringBuilderParam.append(text.substring(index + 1).trim()); + stringBuilderParam.append("
"); + } + if (hasTag) { + return stringBuilderParam.toString(); + } + return ""; + } + + protected String formatReturn(Method methodNode) { + boolean hasTag = false; + StringBuilder stringBuilderParam = new StringBuilder(); + stringBuilderParam.append("
Returns:
"); + for (TagInfo tagInfo : methodNode.getTag()) { + if (!tagInfo.getName().equals("@return")) { + continue; + } + hasTag = true; + String text = tagInfo.getText(); + stringBuilderParam.append("    "); + stringBuilderParam.append(text); + stringBuilderParam.append("
"); + } + if (hasTag) { + return stringBuilderParam.toString(); + } + return ""; + } + + protected String formatSee(Method methodNode) { + boolean hasTag = false; + StringBuilder stringBuilderParam = new StringBuilder(); + stringBuilderParam.append("
See Also:
"); + for (TagInfo tagInfo : methodNode.getTag()) { + if (!tagInfo.getName().equals("@see")) { + continue; + } + hasTag = true; + String camelCasedKeyword = tagInfo.getText(); + int index = camelCasedKeyword.indexOf('#'); + if (index >= 0) { + camelCasedKeyword = camelCasedKeyword.substring(index + 1); + } + camelCasedKeyword = camelCasedKeyword.trim(); + + stringBuilderParam.append("    `"); + char[] camelCasedKeywordArray = camelCasedKeyword.toCharArray(); + stringBuilderParam.append(Character.toUpperCase(camelCasedKeywordArray[0])); + for (int i = 1; i < camelCasedKeywordArray.length; i++) { + if (camelCasedKeywordArray[i] >= 'A' && camelCasedKeywordArray[i] <= 'Z') { + stringBuilderParam.append(' '); + } + stringBuilderParam.append(camelCasedKeywordArray[i]); + } + stringBuilderParam.append("`
"); + } + if (hasTag) { + return stringBuilderParam.toString(); + } + return ""; + } + +} diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/utils/Robotframework.java b/src/main/java/com/github/markusbernhardt/selenium2library/utils/Robotframework.java index 1cb6397..535afbb 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/utils/Robotframework.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/utils/Robotframework.java @@ -6,7 +6,7 @@ import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; -public class Robotframework { +public abstract class Robotframework { public static String getLinkPath(File target, File base) { String path = getPathname(target, base); @@ -22,9 +22,8 @@ public static String encodeURLComponent(final String s) { try { for (int i = 0; i < s.length(); i++) { final char c = s.charAt(i); - if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) - || ((c >= '0') && (c <= '9')) || (c == '-') - || (c == '.') || (c == '_') || (c == '~')) { + if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) + || (c == '-') || (c == '.') || (c == '_') || (c == '~')) { sb.append(c); } else { final byte[] bytes = ("" + c).getBytes("UTF-8"); @@ -32,12 +31,10 @@ public static String encodeURLComponent(final String s) { sb.append('%'); int upper = (((int) b) >> 4) & 0xf; - sb.append(Integer.toHexString(upper).toUpperCase( - Locale.US)); + sb.append(Integer.toHexString(upper).toUpperCase(Locale.US)); int lower = ((int) b) & 0xf; - sb.append(Integer.toHexString(lower).toUpperCase( - Locale.US)); + sb.append(Integer.toHexString(lower).toUpperCase(Locale.US)); } } } @@ -106,8 +103,7 @@ public static String secsToTimestr(double double_secs) { public static double timestrToSecs(String timestr) { timestr = normalizeTimestr(timestr); if (timestr.length() == 0) { - throw new Selenium2LibraryNonFatalException("Invalid timestr: " - + timestr); + throw new Selenium2LibraryNonFatalException("Invalid timestr: " + timestr); } try { @@ -157,11 +153,9 @@ public static double timestrToSecs(String timestr) { } } if (stringBuilder.length() != 0) { - throw new Selenium2LibraryNonFatalException("Invalid timestr: " - + timestr); + throw new Selenium2LibraryNonFatalException("Invalid timestr: " + timestr); } - return sign - * (millis / 1000 + secs + mins * 60 + hours * 60 * 60 + days * 60 * 60 * 24); + return sign * (millis / 1000 + secs + mins * 60 + hours * 60 * 60 + days * 60 * 60 * 24); } public static String normalizeTimestr(String timestr) { diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/utils/TimestrHelper.java b/src/main/java/com/github/markusbernhardt/selenium2library/utils/TimestrHelper.java index d31fe27..df25d78 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/utils/TimestrHelper.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/utils/TimestrHelper.java @@ -5,14 +5,14 @@ public class TimestrHelper { - private boolean compact; - private List ret = new ArrayList(); - private String sign; - private int millis; - private int secs; - private int mins; - private int hours; - private int days; + protected boolean compact; + protected List ret = new ArrayList(); + protected String sign; + protected int millis; + protected int secs; + protected int mins; + protected int hours; + protected int days; public TimestrHelper(double double_secs) { this(double_secs, false); diff --git a/src/main/java/com/github/markusbernhardt/selenium2library/utils/WebDriverCache.java b/src/main/java/com/github/markusbernhardt/selenium2library/utils/WebDriverCache.java index befe405..aca21a5 100644 --- a/src/main/java/com/github/markusbernhardt/selenium2library/utils/WebDriverCache.java +++ b/src/main/java/com/github/markusbernhardt/selenium2library/utils/WebDriverCache.java @@ -49,8 +49,7 @@ public String register(WebDriver webDriver, String alias) { if (closedSessionIds.size() == 0) { // no closed id maxAssignedSessionId++; - currentSessionIdAliasWebDriverTuple.id = Integer - .toString(maxAssignedSessionId); + currentSessionIdAliasWebDriverTuple.id = Integer.toString(maxAssignedSessionId); } else { // reuse closed id currentSessionIdAliasWebDriverTuple.id = closedSessionIds.pop(); @@ -58,11 +57,9 @@ public String register(WebDriver webDriver, String alias) { // store the new tuple openSessionIds.push(currentSessionIdAliasWebDriverTuple.id); - tupleBySessionId.put(currentSessionIdAliasWebDriverTuple.id, - currentSessionIdAliasWebDriverTuple); + tupleBySessionId.put(currentSessionIdAliasWebDriverTuple.id, currentSessionIdAliasWebDriverTuple); if (alias != null) { - tupleByAlias.put(currentSessionIdAliasWebDriverTuple.alias, - currentSessionIdAliasWebDriverTuple); + tupleByAlias.put(currentSessionIdAliasWebDriverTuple.alias, currentSessionIdAliasWebDriverTuple); } return currentSessionIdAliasWebDriverTuple.id; } @@ -94,8 +91,7 @@ public void close() { // Set the last opened webdriver as current webdriver if (openSessionIds.size() != 0) { - currentSessionIdAliasWebDriverTuple = tupleBySessionId - .get(openSessionIds.pop()); + currentSessionIdAliasWebDriverTuple = tupleBySessionId.get(openSessionIds.pop()); } else { currentSessionIdAliasWebDriverTuple = null; } @@ -103,8 +99,7 @@ public void close() { } public void closeAll() { - for (SessionIdAliasWebDriverTuple sessionIdAliasWebDriverTuple : tupleBySessionId - .values()) { + for (SessionIdAliasWebDriverTuple sessionIdAliasWebDriverTuple : tupleBySessionId.values()) { sessionIdAliasWebDriverTuple.webDriver.quit(); } maxAssignedSessionId = 0; @@ -116,8 +111,7 @@ public void closeAll() { } public void switchBrowser(String sessionIdOrAlias) { - SessionIdAliasWebDriverTuple sessionIdAliasWebDriverTuple = tupleByAlias - .get(sessionIdOrAlias); + SessionIdAliasWebDriverTuple sessionIdAliasWebDriverTuple = tupleByAlias.get(sessionIdOrAlias); if (sessionIdAliasWebDriverTuple != null) { currentSessionIdAliasWebDriverTuple = sessionIdAliasWebDriverTuple; openSessionIds.remove(currentSessionIdAliasWebDriverTuple.id); @@ -132,8 +126,7 @@ public void switchBrowser(String sessionIdOrAlias) { openSessionIds.push(currentSessionIdAliasWebDriverTuple.id); return; } - throw new Selenium2LibraryFatalException(String.format( - "Non-existing index or alias '%s'", sessionIdOrAlias)); + throw new Selenium2LibraryFatalException(String.format("Non-existing index or alias '%s'", sessionIdOrAlias)); } public Collection getWebDrivers() { diff --git a/src/main/resources/com/github/markusbernhardt/selenium2library/Selenium2Library.properties b/src/main/resources/com/github/markusbernhardt/selenium2library/Selenium2Library.properties index e5683df..defbd48 100644 --- a/src/main/resources/com/github/markusbernhardt/selenium2library/Selenium2Library.properties +++ b/src/main/resources/com/github/markusbernhardt/selenium2library/Selenium2Library.properties @@ -1 +1 @@ -version=${project.version} \ No newline at end of file +version=${project.version} diff --git a/src/test/robotframework/imports/Google.txt b/src/test/robotframework/imports/Google.txt index 0ee3d28..a2231db 100644 --- a/src/test/robotframework/imports/Google.txt +++ b/src/test/robotframework/imports/Google.txt @@ -1,6 +1,6 @@ *** Settings *** Documentation Defines which objects are available to -... all testcases for the BWM.digital website. +... all testcases for the Google website. Resource ../objects/Google.Search.txt Resource ../objects/Google.Site.txt Resource ../objects/Google.TopNavi.txt diff --git a/src/test/robotframework/objects/Google.Search.txt b/src/test/robotframework/objects/Google.Search.txt index ea93508..198fe94 100644 --- a/src/test/robotframework/objects/Google.Search.txt +++ b/src/test/robotframework/objects/Google.Search.txt @@ -5,11 +5,11 @@ Resource ../adapters/Selenium2Library.txt *** Variables *** ${Google.Search.Locator.SearchField} id=gbqfq -${Google.Search.Locator.SearchResult.Rtomac} xpath=//cite[contains(., 'https://github.com/rtomac/')] +${Google.Search.Locator.SearchResult.Rtomac} xpath=//a[contains(.,'MarkusBernhardt')] *** Keywords *** Search String [Arguments] ${Search.String} Input Text ${Google.Search.Locator.SearchField} ${Search.String} Press Key ${Google.Search.Locator.SearchField} \\13 - Wait Until Element Is Visible ${Google.Search.Locator.SearchResult.Rtomac} \ No newline at end of file + Wait Until Element Is Visible ${Google.Search.Locator.SearchResult.Rtomac} diff --git a/src/test/robotframework/testsuites/UnitTests/Google.txt b/src/test/robotframework/testsuites/UnitTests/Google.txt index 836386c..9d4f96b 100644 --- a/src/test/robotframework/testsuites/UnitTests/Google.txt +++ b/src/test/robotframework/testsuites/UnitTests/Google.txt @@ -9,14 +9,19 @@ Resource ../../imports/Google.txt Open And Close Google Site Capture Page Screenshot +Store Web Element In JavaScript + Execute Javascript window.document.my_element = window.document.getElementsByClassName('gbqfba')[0]; + ${className}= Execute Javascript return window.document.my_element.className; + Should Be Equal ${className} gbqfba + Search Robotframework Selenium2Library - Google.Search.Search String Robotframework Selenium2Library + Google.Search.Search String Robotframework Selenium2Library Java Search With JavaScript Locator Selenium2Library.Add Location Strategy elementById return window.document.getElementById(arguments[0]); - Input Text elementById=gbqfq Robotframework Selenium2Library + Input Text elementById=gbqfq Robotframework Selenium2Library Java Press Key elementById=gbqfq \\13 - Wait Until Element Is Visible xpath=//cite[contains(., 'https://github.com/rtomac/')] + Wait Until Element Is Visible xpath=//a[contains(.,'MarkusBernhardt')] Capture Page Screenshot Search With JavaScript Locator And Delimiter @@ -29,9 +34,9 @@ Search With JavaScript Locator Text Wait Until Element Is Visible text=Google-Suche Search Without Locator Type - Input Text gbqfq Robotframework Selenium2Library + Input Text gbqfq Robotframework Selenium2Library Java Press Key gbqfq \\13 - Wait Until Element Is Visible //cite[contains(., 'https://github.com/rtomac/')] + Wait Until Element Is Visible //a[contains(.,'MarkusBernhardt')] Get Id Of Active Element With JavaScript Input Text gbqfq Robotframework Selenium2Library diff --git a/src/test/robotframework/testsuites/UnitTests/JSEvents.txt b/src/test/robotframework/testsuites/UnitTests/JSEvents.txt index 1f24cf7..30ed5c8 100644 --- a/src/test/robotframework/testsuites/UnitTests/JSEvents.txt +++ b/src/test/robotframework/testsuites/UnitTests/JSEvents.txt @@ -9,6 +9,7 @@ ${URL Application} http://fiddle.jshell.net/ShPVX/show/ *** Keywords *** Open Page Open Browser ${URL Application} + Select Frame xpath=//iframe Wait Until Page Contains Element xpath=//select[@name='test'] *** Test Cases ***