├── .gitignore ├── CHANGES.md ├── COPYRIGHT.txt ├── DEPENDENCIES.md ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main ├── aspect │ └── com │ │ └── github │ │ └── markusbernhardt │ │ └── selenium2library │ │ └── aspects │ │ └── RunOnFailureAspect.aj ├── java │ ├── Selenium2Library.java │ └── com │ │ └── github │ │ └── markusbernhardt │ │ └── selenium2library │ │ ├── RunOnFailureKeywords.java │ │ ├── RunOnFailureKeywordsAdapter.java │ │ ├── Selenium2Library.java │ │ ├── Selenium2LibraryFatalException.java │ │ ├── Selenium2LibraryNonFatalException.java │ │ ├── keywords │ │ ├── BrowserManagement.java │ │ ├── Cookie.java │ │ ├── Element.java │ │ ├── FormElement.java │ │ ├── JavaScript.java │ │ ├── Logging.java │ │ ├── RunOnFailure.java │ │ ├── Screenshot.java │ │ ├── SelectElement.java │ │ ├── TableElement.java │ │ └── Waiting.java │ │ ├── locators │ │ ├── ElementFinder.java │ │ ├── TableElementFinder.java │ │ └── WindowManager.java │ │ └── utils │ │ ├── Javadoc2Libdoc.java │ │ ├── Python.java │ │ ├── Robotframework.java │ │ ├── TimestrHelper.java │ │ └── WebDriverCache.java └── resources │ └── com │ └── github │ └── markusbernhardt │ └── selenium2library │ └── Selenium2Library.properties └── test └── robotframework ├── adapters └── Selenium2Library.txt ├── imports └── Google.txt ├── objects ├── Google.Search.txt ├── Google.Site.txt └── Google.TopNavi.txt ├── settings ├── Settings.txt └── profiles │ ├── Jenkins.txt │ └── Local.txt └── testsuites └── UnitTests ├── AW3Schools.txt ├── ExtJS.txt ├── GetInnerElementId.txt ├── Google.txt ├── JSEvents.txt └── WhatAreCookies.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .settings 3 | .classpath 4 | target -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | CHANGES 2 | ======= 3 | 4 | Working on 5 | ---------- 6 | * Fixed 'Unable to open firefox when passing in desiredCapabilities and browserOptions arguments'. See #66, #75, #76. 7 | 8 | Unreleased 9 | ---------- 10 | 11 | 1.4.0.8 12 | ------- 13 | * Updating robotframework to version 2.9.2 14 | * Updating selenium-server to version 2.48.2 15 | * Updating xml-doclet to version 1.0.5 16 | * Updating aspectjrt to version 1.8.7 17 | * Updating selendroid-client to version 0.17.0 18 | * Updating java-client to version 3.2.0 19 | * Fixed 'Library not loading with Robot Framework 2.9'. See #72, #73, #74. Thanks to Hi-FI !!! 20 | * Fixed 'Selenium update (v 2.48.2)'. See #65, #77 21 | 22 | 1.4.0.7 23 | ------- 24 | * Avoid NullPointerException in isEnabled method. See #53, #56. Great thanks to atcarmo for this patch. 25 | * Updated selenium-server dependency to 2.43.1. See #58. Great thanks to WojtekKowaluk for this patch. 26 | * Corrected Google Search word 27 | * Fixed JSEvents test 28 | * Added Selendroid 29 | * Added Appium 30 | * Allow subclassing of Selenium2Library by moving it to a package. 31 | 32 | 1.4.0.6 33 | ------- 34 | * Fixed 'Select Window' always selecting the last window and not the desired window. 35 | 36 | 1.4.0.5 37 | ------- 38 | * Fixed the use of == to compare Strings in FormElement. See #49 39 | * Fixed 'Get Alert Message'. See #50 40 | * 'Get Alert Message' does not confirm the Alert any more. 'Get Alert Message' + Confirm = 'Confirm Action'. 41 | * Added keyword 'Choose Cancel On Confirmation' 42 | * Added keyword 'Choose Ok On Confirmation' 43 | * Fixed 'Select Window' failing, when the current window is already closed. See #48 44 | 45 | 1.4.0.4 46 | ------- 47 | * Update xml-doclet to 1.0.4 48 | * Make selenium2library-java 1.6 compliant. See #44 49 | 50 | 1.4.0.3 51 | ------- 52 | * Update robotframework to 2.8.3 53 | * Fixed a UnicodeDecodeError at logging strings containing backslashes. 54 | 55 | 1.4.0.2 56 | ------- 57 | 58 | * Update selenium to 2.39.0 59 | * Update phantomjsdriver to 1.1.0 60 | * DesiredCapabilities can be specified as complex JSON strings now 61 | * Removed the ffProfileDir argument from Open Browser 62 | * Added browserOptions argument to Open Browser 63 | 64 | 1.4.0.1 65 | ------- 66 | 67 | * Fixed wrong separator in Javadoc2Libdoc. See #40 68 | * Fixed Page Should Contain Button fails to find buttons with the button tag. See #43 69 | 70 | 1.4.0.0 71 | ------- 72 | 73 | * Porting recent changes in the master branch of the Python Selenium2Library 74 | from id b4a3e500 until cf971d91 to this Java port. This contains roughly: 75 | * Added 'Get Window Size' and 'Set Window Size' keywords matching the Selenium functionality. 76 | * Added new keyword 'Click Element At Coordinates'. 77 | * Added keywords for verifying text entered into textarea elements. 78 | * 'Textarea Should Contain' 79 | * 'Textarea Should Not Contain' 80 | * 'Textarea Value Should Be' 81 | * 'Textarea Value Should Not Be' 82 | * 'Mouse Up' doesn't click any more on the element. 83 | * Raise exception in selecting non-existing item in list. Error handling varies 84 | between single-select and multi-select lists. See keyword documentation for 85 | more information. 86 | * Back-port recent changes from version 1.3 and 1.4 of Python library. See #35 87 | * Jump Version number to 1.4.0.0 to reflect the new version of the Python library. 88 | 89 | 1.2.0.14 90 | -------- 91 | 92 | * Fixed an ArrayIndexOutOfBoundsException in the Select Window keyword. See #27 93 | * Added the possibility to set logging directory. See #28. 94 | * Refactoring of the library to a JavaLibCore AnnotationLibrary. See #28. 95 | * Added keyword documentation from library for e.g. Ride. See #16, #28. 96 | * Added keyword missing documentation for new keywords. See #32. 97 | * Added access to the current WebDriver instance from custom libraries. #30 98 | * Generating libdoc from javadoc. 99 | 100 | 1.2.0.13 101 | -------- 102 | 103 | * Fixed a NullPointerException in Capture Page Screenshot when Log File is set to NONE. See #24 104 | * Fixed that library can't be instrumented with JaCoCo. See #22 105 | * Update robotframework to 2.8.1 106 | * Update robotframework-maven-plugin to 1.2 107 | * Update aspectj to 1.7.3 108 | * Update java.version to 1.7 109 | 110 | 1.2.0.12 111 | -------- 112 | 113 | * Fixed a NullPointerException in Close Browser. See #23 114 | 115 | 1.2.0.11 116 | -------- 117 | 118 | * Fixed a NullPointerException in WebDriverCache.getCurrent. See #21 119 | 120 | 1.2.0.10 121 | -------- 122 | 123 | * Fixed a TypeError in List Selection Should Be. See #18. 124 | * Fixed a problem with logging a string longer then 1024 characters on Windows systems. See #17. 125 | * Porting recent changes in the master branch of the Python Selenium2Library 126 | from id 6793340d until b4a3e500 to this Java port. This contains roughly: 127 | * Keyword 'Current Frame Should Not Contain' 128 | * Tag 'option' also marks an form element 129 | 130 | 131 | 1.2.0.9 132 | ------- 133 | 134 | * Updated Selenium to 2.33.0 135 | * Updated Robot Framework to 2.8 136 | * Fixing a bug in "Select From List" that leads to traversing all list entries before selecting 137 | the correct one. See issue #12. 138 | * New keywords: 139 | * Get Remote Capabilities 140 | * Log Remote Capabilities 141 | * Get System Info 142 | * Log System Info 143 | 144 | 1.2.0.8 145 | ------- 146 | 147 | * More sophisticated algorithm to create new session ids at Open Browser. See issue #11 and #14. 148 | 149 | 1.2.0.7 150 | ------- 151 | 152 | * Fixing a ConcurrentModificationException when using the keyword 'Close All Browsers'. See issue #11. 153 | 154 | 1.2.0.6 155 | ------- 156 | 157 | * Fixing a IllegalArgumentException when using the keyword 'Select From List'. See issue #8. 158 | * Renamed keyword "Textfield Should Be" to "Textfield Value Should Be" to be in sync with Python library. See issue #9. 159 | * New keywords "Get Remote Session Id" and "Log Remote Session Id". See issue #10. 160 | 161 | 1.2.0.5 162 | ------- 163 | 164 | * Fixing stupid bugs in the new logging functionality 165 | 166 | 1.2.0.4 167 | ------- 168 | 169 | * Log messages larger than 1kB are now written to a temp file on the Java side and then read back 170 | on the Python side. This avoids long messages to get parsed by the Jython source code parser. 171 | See issue #7. 172 | 173 | 1.2.0.3 174 | ------- 175 | 176 | * Rerelease of 1.2.0.2, because the upload to maven central was corrupt 177 | 178 | 1.2.0.2 179 | ------- 180 | 181 | * Support for iPhone, iPad and Android started. See issue #6. 182 | 183 | 1.2.0.1 184 | ------- 185 | 186 | * Added a delimiter to custom locator strategies to enable multiple arguments. See issue #3. 187 | 188 | 1.2.0.0 189 | ------- 190 | 191 | * Upgrading to version 1.2.0 of the Python Selenium2Library 192 | * Porting recent changes in the master branch of the Python Selenium2Library 193 | from id 2d3adce8 until 6793340d to this Java port. This contains roughly: 194 | * Add Cookie keyword 195 | * Fixed 'Get Selected List Label' under IE7 or IE8. 196 | 197 | 1.1.0.7 198 | ------- 199 | 200 | * Fixing a problem at opening a Safari, Opera or PhantomJS browser that caused a Selenium2LibraryFatalException("... is not a supported browser.") 201 | * Fixing a problem with screenshots not showing in the log 202 | * Added a jar with all required dependencies to the deployed packages 203 | 204 | 1.1.0.6 205 | ------- 206 | 207 | * ArrayIndexOutOfBoundsException at locators without prefix fixed. 208 | * Default Selenium dependency updated to 2.32.0 209 | * Support for Safari browser 210 | * Porting recent changes in the master branch of the Python Selenium2Library 211 | from id 966a4c5f until 2d3adce8 to this Java port. This contains roughly: 212 | * Support for PhantomJS browser 213 | * jquery/sizzle locator 214 | 215 | 216 | 1.1.0.5 217 | ------- 218 | 219 | * Added the following keyword: 220 | * Add Location Strategy 221 | 222 | This keyword is somewhat analogous to the SeleniumLibrary (RC) keyword. 223 | It can be used to register a JavaScript function as locator. 224 | * Changed all ThreadLocal to static 225 | 226 | 227 | 1.1.0.4 228 | ------- 229 | 230 | * Added proxy handling for RemoteWebDriver 231 | * Minor fixes 232 | 233 | 234 | 1.1.0.3 235 | ------- 236 | 237 | * Changed the wrongly set artifactId from selenium2library to 238 | robotframework-selenium2library-java. 239 | 240 | 241 | 1.1.0.2 242 | ------- 243 | 244 | * Extended com.github.markusbernhardt.selenium2library.keywords.Waiting.waitUntil 245 | to keep waiting, when a Throwable is thrown. 246 | * Added the following keyword: 247 | * Wait Until Element Is Successfully Clicked 248 | 249 | In a AJAX site it sometimes happens, that an element disappears between 250 | a call to Wait Until Element Is Visible and Click Element. This keyword 251 | simply tries to click on the element until it worked or the timeout 252 | occurs. -------------------------------------------------------------------------------- /COPYRIGHT.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2008-2011 Nokia Siemens Networks Oyj 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | -------------------------------------------------------------------------------- /DEPENDENCIES.md: -------------------------------------------------------------------------------- 1 | [INFO] ------------------------------------------------------------------------ 2 | [INFO] Building Robot Framework :: Selenium2Library 1.4.0.8-SNAPSHOT 3 | [INFO] ------------------------------------------------------------------------ 4 | [INFO] 5 | [INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ robotframework-selenium2library-java --- 6 | [INFO] com.github.markusbernhardt:robotframework-selenium2library-java:jar:1.4.0.8-SNAPSHOT 7 | [INFO] +- org.robotframework:javalib-core:jar:1.2.1:compile 8 | [INFO] | +- commons-collections:commons-collections:jar:3.2:compile 9 | [INFO] | \- com.thoughtworks.paranamer:paranamer:jar:1.1.2:compile 10 | [INFO] +- com.github.markusbernhardt:xml-doclet:jar:1.0.5:compile 11 | [INFO] | +- com.sun:tools:jar:1.7:system 12 | [INFO] | +- commons-cli:commons-cli:jar:1.2:compile 13 | [INFO] | \- org.slf4j:slf4j-api:jar:1.7.12:compile 14 | [INFO] +- org.seleniumhq.selenium:selenium-server:jar:2.48.2:compile 15 | [INFO] | +- com.beust:jcommander:jar:1.48:compile 16 | [INFO] | +- org.bouncycastle:bcprov-jdk15on:jar:1.48:compile 17 | [INFO] | +- org.bouncycastle:bcpkix-jdk15on:jar:1.48:compile 18 | [INFO] | +- mx4j:mx4j-tools:jar:3.0.1:compile 19 | [INFO] | +- org.mortbay.jetty:servlet-api-2.5:jar:6.1.9:compile 20 | [INFO] | +- org.seleniumhq.selenium:jetty-repacked:jar:9.2.13.v20150730:compile 21 | [INFO] | +- javax.servlet:javax.servlet-api:jar:3.1.0:compile 22 | [INFO] | +- org.seleniumhq.selenium:jetty-rc-repacked:jar:5:compile 23 | [INFO] | +- net.jcip:jcip-annotations:jar:1.0:compile 24 | [INFO] | +- org.seleniumhq.selenium:selenium-java:jar:2.48.2:compile 25 | [INFO] | | +- org.seleniumhq.selenium:selenium-chrome-driver:jar:2.48.2:compile 26 | [INFO] | | +- org.seleniumhq.selenium:selenium-edge-driver:jar:2.48.2:compile 27 | [INFO] | | | \- commons-io:commons-io:jar:2.4:compile 28 | [INFO] | | +- org.seleniumhq.selenium:selenium-htmlunit-driver:jar:2.48.2:compile 29 | [INFO] | | | \- net.sourceforge.htmlunit:htmlunit:jar:2.18:compile 30 | [INFO] | | | +- xalan:xalan:jar:2.7.2:compile 31 | [INFO] | | | | \- xalan:serializer:jar:2.7.2:compile 32 | [INFO] | | | +- org.apache.commons:commons-lang3:jar:3.4:compile 33 | [INFO] | | | +- org.apache.httpcomponents:httpmime:jar:4.5:compile 34 | [INFO] | | | +- net.sourceforge.htmlunit:htmlunit-core-js:jar:2.17:compile 35 | [INFO] | | | +- xerces:xercesImpl:jar:2.11.0:compile 36 | [INFO] | | | | \- xml-apis:xml-apis:jar:1.4.01:compile 37 | [INFO] | | | +- net.sourceforge.nekohtml:nekohtml:jar:1.9.22:compile 38 | [INFO] | | | +- net.sourceforge.cssparser:cssparser:jar:0.9.16:compile 39 | [INFO] | | | | \- org.w3c.css:sac:jar:1.3:compile 40 | [INFO] | | | \- org.eclipse.jetty.websocket:websocket-client:jar:9.2.12.v20150709:compile 41 | [INFO] | | | +- org.eclipse.jetty:jetty-util:jar:9.2.12.v20150709:compile 42 | [INFO] | | | +- org.eclipse.jetty:jetty-io:jar:9.2.12.v20150709:compile 43 | [INFO] | | | \- org.eclipse.jetty.websocket:websocket-common:jar:9.2.12.v20150709:compile 44 | [INFO] | | | \- org.eclipse.jetty.websocket:websocket-api:jar:9.2.12.v20150709:compile 45 | [INFO] | | +- org.seleniumhq.selenium:selenium-firefox-driver:jar:2.48.2:compile 46 | [INFO] | | +- org.seleniumhq.selenium:selenium-ie-driver:jar:2.48.2:compile 47 | [INFO] | | +- org.seleniumhq.selenium:selenium-safari-driver:jar:2.48.2:compile 48 | [INFO] | | +- org.seleniumhq.selenium:selenium-support:jar:2.48.2:compile 49 | [INFO] | | +- org.webbitserver:webbit:jar:0.4.14:compile 50 | [INFO] | | | \- io.netty:netty:jar:3.5.2.Final:compile 51 | [INFO] | | \- org.seleniumhq.selenium:selenium-leg-rc:jar:2.48.2:compile 52 | [INFO] | +- org.yaml:snakeyaml:jar:1.8:compile 53 | [INFO] | \- commons-codec:commons-codec:jar:1.10:compile 54 | [INFO] +- com.opera:operadriver:jar:1.5:compile 55 | [INFO] | +- com.opera:operalaunchers:jar:1.1:compile 56 | [INFO] | +- com.google.protobuf:protobuf-java:jar:2.4.1:compile 57 | [INFO] | +- com.google.guava:guava:jar:14.0:compile 58 | [INFO] | +- commons-jxpath:commons-jxpath:jar:1.3:compile 59 | [INFO] | \- org.ini4j:ini4j:jar:0.5.2:compile 60 | [INFO] +- com.github.detro.ghostdriver:phantomjsdriver:jar:1.1.0:compile 61 | [INFO] +- org.robotframework:robotframework:jar:2.9.2:compile 62 | [INFO] +- org.aspectj:aspectjrt:jar:1.8.7:compile 63 | [INFO] +- com.googlecode.json-simple:json-simple:jar:1.1.1:compile 64 | [INFO] | \- junit:junit:jar:4.10:compile 65 | [INFO] | \- org.hamcrest:hamcrest-core:jar:1.1:compile 66 | [INFO] +- org.seleniumhq.selenium:selenium-remote-driver:jar:2.48.2:compile 67 | [INFO] | +- cglib:cglib-nodep:jar:2.1_3:compile 68 | [INFO] | +- com.google.code.gson:gson:jar:2.3.1:compile 69 | [INFO] | +- org.seleniumhq.selenium:selenium-api:jar:2.48.2:compile 70 | [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.5.1:compile 71 | [INFO] | | +- org.apache.httpcomponents:httpcore:jar:4.4.3:compile 72 | [INFO] | | \- commons-logging:commons-logging:jar:1.2:compile 73 | [INFO] | +- net.java.dev.jna:jna:jar:4.1.0:compile 74 | [INFO] | \- net.java.dev.jna:jna-platform:jar:4.1.0:compile 75 | [INFO] +- io.selendroid:selendroid-client:jar:0.17.0:compile 76 | [INFO] | +- io.selendroid:selendroid-common:jar:0.17.0:compile 77 | [INFO] | | \- org.json:json:jar:20090211:compile 78 | [INFO] | \- io.selendroid:selendroid-server-common:jar:0.17.0:compile 79 | [INFO] | \- io.netty:netty-all:jar:4.0.21.Final:compile 80 | [INFO] +- io.appium:java-client:jar:3.2.0:compile 81 | [INFO] | +- cglib:cglib:jar:3.1:compile 82 | [INFO] | | \- org.ow2.asm:asm:jar:4.2:compile 83 | [INFO] | \- commons-validator:commons-validator:jar:1.4.1:compile 84 | [INFO] | +- commons-beanutils:commons-beanutils:jar:1.8.3:compile 85 | [INFO] | \- commons-digester:commons-digester:jar:1.8.1:compile 86 | [INFO] \- org.apache.commons:commons-exec:jar:1.3:compile 87 | [INFO] ------------------------------------------------------------------------ 88 | [INFO] BUILD SUCCESS 89 | [INFO] ------------------------------------------------------------------------ 90 | [INFO] Total time: 1.000 s 91 | [INFO] Finished at: 2015-10-27T10:31:41+01:00 92 | [INFO] Final Memory: 19M/309M 93 | [INFO] ------------------------------------------------------------------------ -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Java port of the Selenium 2 (WebDriver) Python library for Robot Framework 2 | ========================================================================== 3 | 4 | Repository moved 5 | ------------ 6 | Maintainer of the library is changed, and new repository is at https://github.com/Hi-Fi/robotframework-seleniumlibrary-java. 7 | 8 | Introduction 9 | ------------ 10 | 11 | Selenium2Library is a web testing library for Robot Framework that leverages 12 | the [Selenium 2 (WebDriver)](http://docs.seleniumhq.org/docs/03_webdriver.jsp) 13 | libraries from the [Selenium](http://docs.seleniumhq.org) project. 14 | It is modeled after (and forked from) the Robot Framework 15 | [SeleniumLibrary](http://code.google.com/p/robotframework-seleniumlibrary/) 16 | library, but re-implemented to use Selenium 2 and WebDriver technologies. 17 | * More information about this library can be found in the 18 | [Keyword Documentation](http://search.maven.org/remotecontent?filepath=com/github/markusbernhardt/robotframework-selenium2library-java/1.4.0.8/robotframework-selenium2library-java-1.4.0.8-libdoc.html). 19 | * For keyword completion in RIDE you can download this 20 | [Library Specs](http://search.maven.org/remotecontent?filepath=com/github/markusbernhardt/robotframework-selenium2library-java/1.4.0.8/robotframework-selenium2library-java-1.4.0.8-libdoc.xml) 21 | and place it in your PYTHONPATH. 22 | 23 | This Java port of the existing Selenium2Library was created to enable 24 | the usage of a Selenium 2 library with Jython. 25 | * Python Selenium2Library needs Python 2.6 upwards 26 | * The latests stable release of Jython is 2.5.3 27 | * Jython 2.7b2 is a beta version 28 | * There seems to be only slow development of a stable Jython 2.7 29 | 30 | Usage 31 | ----- 32 | 33 | This library is a direct replacement to the Python Selenium2Library. 34 | There are almost no changes necesssary to the existing code. You 35 | can execute the same testcases and keywords with Python and Jython. 36 | 37 | If you are using the robotframework-maven-plugin you can 38 | use this library by adding the following dependency to 39 | your pom.xml: 40 | 41 | 42 | com.github.markusbernhardt 43 | robotframework-selenium2library-java 44 | 1.4.0.8 45 | test 46 | 47 | 48 | If you cannot use the robotframework-maven-plugin you can use the 49 | [jar-with-dependencies](http://search.maven.org/remotecontent?filepath=com/github/markusbernhardt/robotframework-selenium2library-java/1.4.0.8/robotframework-selenium2library-java-1.4.0.8-jar-with-dependencies.jar), 50 | which contains all required libraries. 51 | 52 | If you want more control and feel adventurous you could you use this 53 | [jar](http://search.maven.org/remotecontent?filepath=com/github/markusbernhardt/robotframework-selenium2library-java/1.4.0.8/robotframework-selenium2library-java-1.4.0.8.jar) 54 | and provide all required libraries from this [list](DEPENDENCIES.md) on your own. 55 | 56 | Differences 57 | ----------- 58 | 59 | * Selenium Speed not implemented 60 | 61 | Setting the Selenium Speed is deprecated several years and not 62 | implemented in WebDriver. The Python Selenium2Library tries to 63 | emulate the old behavior. I have not implemented this emulation 64 | for the following reasons. 65 | 66 | * As far as I understand the emulation is broken and only works 67 | with RemoteWebDriver 68 | * I do not know how to implement that in a correct way with Java 69 | * There is a reason, why this is not implemented in WebDriver. 70 | It's a bad idea. 71 | 72 | Demo 73 | ---- 74 | 75 | This is a maven project. If you have firefox installed, 76 | you can execute the unit tests with: 77 | 78 | mvn integration-test 79 | 80 | Getting Help 81 | ------------ 82 | 83 | The [user group for Robot Framework](https://groups.google.com/forum/#!forum/robotframework-users) 84 | is the best place to get help. Consider including in the post: 85 | * Full description of what you are trying to do and expected outcome 86 | * Version number of Selenium2Library, Robot Framework, and Selenium 87 | * StackTraces or other debug output containing error information 88 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | org.sonatype.oss 7 | oss-parent 8 | 7 9 | 10 | 11 | com.github.markusbernhardt 12 | robotframework-selenium2library-java 13 | 1.4.0.9 14 | jar 15 | 16 | Robot Framework :: Selenium2Library 17 | Java port of the Selenium 2 (WebDriver) Python library for Robot Framework 18 | https://github.com/MarkusBernhardt/robotframework-selenium2library-java 19 | 20 | 21 | UTF-8 22 | true 23 | true 24 | 1.8.7 25 | 1.6 26 | 1.0.5 27 | 2.9.2 28 | 2.48.2 29 | Selenium2Library 30 | 31 | 32 | 33 | https://github.com/MarkusBernhardt/robotframework-selenium2library-java 34 | scm:git://github.com/MarkusBernhardt/robotframework-selenium2library-java.git 35 | scm:git:ssh://git@github.com/MarkusBernhardt/robotframework-selenium2library-java.git 36 | 37 | 38 | 39 | 40 | 41 | 42 | The Apache Software License, Version 2.0 43 | http://www.apache.org/licenses/LICENSE-2.0.txt 44 | repo 45 | 46 | 47 | 48 | 49 | 50 | com.github.hi-fi 51 | robotframework-seleniumlibrary 52 | 2.48.2.0 53 | 54 | 55 | 56 | 57 | 58 | Markus Bernhardt 59 | markus.bernhardt@me.com 60 | 61 | 62 | 63 | 64 | 65 | org.robotframework 66 | javalib-core 67 | 1.2.1 68 | 69 | 70 | com.github.markusbernhardt 71 | xml-doclet 72 | ${xml.doclet.version} 73 | 74 | 75 | org.seleniumhq.selenium 76 | selenium-server 77 | ${selenium.version} 78 | 79 | 80 | com.opera 81 | operadriver 82 | 1.5 83 | 84 | 85 | com.github.detro.ghostdriver 86 | phantomjsdriver 87 | 1.1.0 88 | 89 | 90 | org.robotframework 91 | robotframework 92 | ${robotframework.version} 93 | 94 | 95 | org.aspectj 96 | aspectjrt 97 | ${aspectj.version} 98 | 99 | 100 | com.googlecode.json-simple 101 | json-simple 102 | 1.1.1 103 | 104 | 105 | org.seleniumhq.selenium 106 | selenium-remote-driver 107 | ${selenium.version} 108 | 109 | 110 | io.selendroid 111 | selendroid-client 112 | 0.17.0 113 | 114 | 115 | io.appium 116 | java-client 117 | 3.2.0 118 | 119 | 120 | org.apache.commons 121 | commons-exec 122 | 1.3 123 | 124 | 125 | 126 | 127 | 128 | 129 | src/main/resources 130 | true 131 | 132 | **/*.properties 133 | **/*.xml 134 | 135 | 136 | 137 | src/main/resources 138 | false 139 | 140 | **/*.properties 141 | **/*.xml 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | org.codehaus.mojo 150 | aspectj-maven-plugin 151 | 1.4 152 | 153 | 154 | 155 | compile 156 | 157 | 158 | 159 | 160 | ${java.version} 161 | ${java.version} 162 | ${java.version} 163 | true 164 | 165 | 166 | 167 | org.aspectj 168 | aspectjtools 169 | ${aspectj.version} 170 | 171 | 172 | 173 | 174 | 175 | org.robotframework 176 | robotframework-maven-plugin 177 | 1.4.5 178 | 179 | 180 | test 181 | integration-test 182 | 183 | run 184 | 185 | 186 | debug 187 | ${project.basedir}/src/test/robotframework/testsuites 188 | 189 | 190 | 191 | html 192 | package 193 | 194 | libdoc 195 | 196 | 197 | 198 | ${project.build.directory} 199 | ${keywords.class}.html 200 | ${keywords.class} 201 | 202 | 203 | 204 | 205 | xml 206 | package 207 | 208 | libdoc 209 | 210 | 211 | 212 | ${project.build.directory} 213 | ${keywords.class}.xml 214 | ${keywords.class} 215 | 216 | 217 | 218 | 219 | 220 | 221 | org.robotframework 222 | robotframework 223 | ${robotframework.version} 224 | 225 | 226 | 227 | 228 | 229 | org.apache.maven.plugins 230 | maven-compiler-plugin 231 | 3.3 232 | 233 | ${java.version} 234 | ${java.version} 235 | ${project.build.sourceEncoding} 236 | true 237 | true 238 | javac 239 | 240 | 241 | 242 | 243 | org.apache.maven.plugins 244 | maven-assembly-plugin 245 | 2.6 246 | 247 | 248 | jar-with-dependencies 249 | 250 | 251 | 252 | 253 | make-assembly 254 | package 255 | 256 | single 257 | 258 | 259 | 260 | 261 | 262 | 263 | org.apache.maven.plugins 264 | maven-javadoc-plugin 265 | 2.10.3 266 | 267 | 268 | xml-doclet 269 | prepare-package 270 | 271 | javadoc 272 | 273 | 274 | com.github.markusbernhardt.xmldoclet.XmlDoclet 275 | -d ${project.build.directory}/classes -filename 276 | com/github/markusbernhardt/selenium2library/Selenium2Library.javadoc 277 | false 278 | ${java.home}/../bin 279 | 280 | *.java 281 | com/github/markusbernhardt/selenium2library/Selenium2Library.java 282 | com/github/markusbernhardt/selenium2library/keywords/*.java 283 | 284 | 285 | com.github.markusbernhardt 286 | xml-doclet 287 | ${xml.doclet.version} 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | org.eclipse.m2e 299 | lifecycle-mapping 300 | 1.0.0 301 | 302 | 303 | 304 | 305 | 306 | org.codehaus.mojo 307 | aspectj-maven-plugin 308 | [1.0.0,) 309 | 310 | compile 311 | test-compile 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | org.apache.maven.plugins 321 | maven-enforcer-plugin 322 | [1.0.0,) 323 | 324 | enforce 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | sonatype-oss-release 342 | 343 | 344 | 345 | org.apache.maven.plugins 346 | maven-javadoc-plugin 347 | 2.10.3 348 | 349 | 350 | attach-javadocs 351 | 352 | jar 353 | 354 | 355 | false 356 | 357 | http://java.sun.com/javase/7/docs/api/ 358 | 359 | 360 | 361 | 362 | 363 | 364 | org.codehaus.mojo 365 | build-helper-maven-plugin 366 | 1.9.1 367 | 368 | 369 | attach-artifacts 370 | package 371 | 372 | attach-artifact 373 | 374 | 375 | 376 | 377 | ${project.build.directory}/${keywords.class}.html 378 | html 379 | libdoc 380 | 381 | 382 | ${project.build.directory}/${keywords.class}.xml 383 | xml 384 | libdoc 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | -------------------------------------------------------------------------------- /src/main/aspect/com/github/markusbernhardt/selenium2library/aspects/RunOnFailureAspect.aj: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.aspects; 2 | 3 | import org.aspectj.lang.JoinPoint; 4 | import org.aspectj.lang.annotation.AfterThrowing; 5 | 6 | import com.github.markusbernhardt.selenium2library.RunOnFailureKeywords; 7 | 8 | public aspect RunOnFailureAspect { 9 | 10 | private static ThreadLocal lastThrowable = new ThreadLocal(); 11 | 12 | pointcut handleThrowable() : 13 | execution(public * com.github.markusbernhardt.selenium2library.keywords.*.*(..)); 14 | 15 | after() throwing(Throwable t) : handleThrowable() { 16 | if (lastThrowable.get() == t) { 17 | // Already handled this Throwable 18 | return; 19 | } 20 | 21 | ((RunOnFailureKeywords) thisJoinPoint.getTarget()).runOnFailureByAspectJ(); 22 | lastThrowable.set(t); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/Selenium2Library.java: -------------------------------------------------------------------------------- 1 | public class Selenium2Library extends com.github.markusbernhardt.selenium2library.Selenium2Library { 2 | 3 | public Selenium2Library(String timeout, String implicitWait, String runOnFailure) { 4 | super(timeout, implicitWait, runOnFailure); 5 | } 6 | 7 | public Selenium2Library(String timeout, String implicitWait) { 8 | super(timeout, implicitWait); 9 | } 10 | 11 | public Selenium2Library(String timeout) { 12 | super(timeout); 13 | } 14 | 15 | public Selenium2Library() { 16 | super(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/RunOnFailureKeywords.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library; 2 | 3 | public interface RunOnFailureKeywords { 4 | 5 | /** 6 | * This method is called by the 7 | * com.github.markusbernhardt.selenium2library.aspects.RunOnFailureAspect in 8 | * case a exception is thrown in one of the public methods of a keyword 9 | * class. 10 | */ 11 | void runOnFailureByAspectJ(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/RunOnFailureKeywordsAdapter.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library; 2 | 3 | import org.robotframework.javalib.annotation.Autowired; 4 | 5 | import com.github.markusbernhardt.selenium2library.keywords.RunOnFailure; 6 | 7 | public abstract class RunOnFailureKeywordsAdapter implements RunOnFailureKeywords { 8 | 9 | @Autowired 10 | private RunOnFailure runOnFailure; 11 | 12 | /** 13 | * This method is called by the 14 | * com.github.markusbernhardt.selenium2library.aspects.RunOnFailureAspect in 15 | * case a exception is thrown in one of the public methods of a keyword 16 | * class. 17 | */ 18 | @Override 19 | public void runOnFailureByAspectJ() { 20 | runOnFailure.runOnFailure(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/Selenium2Library.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library; 2 | 3 | import java.io.File; 4 | import java.util.ResourceBundle; 5 | 6 | import javax.script.ScriptEngine; 7 | import javax.script.ScriptEngineManager; 8 | import javax.script.ScriptException; 9 | 10 | import org.robotframework.javalib.annotation.Autowired; 11 | import org.robotframework.javalib.library.AnnotationLibrary; 12 | 13 | import com.github.markusbernhardt.selenium2library.keywords.BrowserManagement; 14 | import com.github.markusbernhardt.selenium2library.keywords.Cookie; 15 | import com.github.markusbernhardt.selenium2library.keywords.Element; 16 | import com.github.markusbernhardt.selenium2library.keywords.FormElement; 17 | import com.github.markusbernhardt.selenium2library.keywords.JavaScript; 18 | import com.github.markusbernhardt.selenium2library.keywords.Logging; 19 | import com.github.markusbernhardt.selenium2library.keywords.RunOnFailure; 20 | import com.github.markusbernhardt.selenium2library.keywords.Screenshot; 21 | import com.github.markusbernhardt.selenium2library.keywords.SelectElement; 22 | import com.github.markusbernhardt.selenium2library.keywords.TableElement; 23 | import com.github.markusbernhardt.selenium2library.keywords.Waiting; 24 | import com.github.markusbernhardt.selenium2library.utils.Javadoc2Libdoc; 25 | 26 | /** 27 | * Selenium2Library is a web testing library for the Robot Framework and was 28 | * originally written in Python. This is the Java port of the Selenium 2 29 | * (WebDriver) Python library for Robot Framework. It uses the Selenium 2 30 | * (WebDriver) libraries internally to control a web browser. See WebDriver for more 32 | * information on Selenium 2 and WebDriver. It runs tests in a real browser 33 | * instance and should work with most modern browsers and can be used with the 34 | * Jython interpreter or any other Java application.
35 | *
36 | * Before running tests
37 | * Prior to running test cases using Selenium2Library, the library must be 38 | * imported into your Robot Framework test suite (see importing section), and 39 | * the `Open Browser` keyword must be used to open a browser to the desired 40 | * location.
41 | *
42 | * Locating elements
43 | * All keywords in Selenium2Library that need to find an element on the page 44 | * take an locator argument.
45 | *
46 | * Key attributes
47 | * By default, when a locator value is provided, it is matched against the key 48 | * attributes of the particular element type. The attributes id and 49 | * name are key attributes to all elements.
50 | *
51 | * List of key attributes: 52 | * 53 | * 54 | * 55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | * 62 | * 63 | * 64 | * 65 | * 66 | * 67 | * 68 | * 69 | * 70 | * 71 | * 72 | * 73 | * 74 | * 75 | * 76 | * 77 | *
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
78 | *
79 | * Example: 80 | * 81 | * 82 | * 83 | * 84 | * 85 | *
Click Elementmy_element
86 | *
87 | * Locator strategies
88 | * It is also possible to specify the approach Selenium2Library should take to 89 | * find an element by specifying a locator strategy with a locator prefix.
90 | *
91 | * Supported strategies are: 92 | * 93 | * 94 | * 95 | * 96 | * 97 | * 98 | * 99 | * 100 | * 101 | * 102 | * 103 | * 104 | * 105 | * 106 | * 107 | * 108 | * 109 | * 110 | * 111 | * 112 | * 113 | * 114 | * 115 | * 116 | * 117 | * 118 | * 119 | * 120 | * 121 | * 122 | * 123 | * 124 | * 125 | * 126 | * 127 | * 128 | * 129 | * 130 | * 131 | * 132 | * 133 | * 134 | * 135 | * 136 | * 137 | * 138 | * 139 | * 140 | * 141 | * 142 | * 143 | * 144 | * 145 | * 146 | * 147 | * 148 | *
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
149 | *
150 | * Locating tables
151 | * Table related keywords, such as `Table Should Contain`, work differently. By 152 | * default, when a table locator value is provided, it will search for a table 153 | * with the specified id attribute.
154 | *
155 | * Example:
156 | * 157 | * 158 | * 159 | * 160 | * 161 | * 162 | *
Table Should Containmy_tabletext
163 | *
164 | * More complex table locator strategies: 165 | * 166 | * 167 | * 168 | * 169 | * 170 | * 171 | * 172 | * 173 | * 174 | * 175 | * 176 | * 177 | * 178 | * 179 | * 180 | * 181 | *
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
182 | *
183 | * Custom location strategies
184 | * It is also possible to register custom location strategies. See `Add Location 185 | * Strategy` for details about custom location strategies.
186 | *
187 | * Example: 188 | * 189 | * 190 | * 191 | * 192 | * 193 | * 194 | * 195 | * 196 | * 197 | * 198 | * 199 | *
Add Location Strategycustomreturn window.document.getElementById(arguments[0]);
Input Textcustom=firstNameMax
200 | *
201 | * Timeouts
202 | * There are several Wait ... keywords that take timeout as an argument. 203 | * All of these timeout arguments are optional. The timeout used by all of them 204 | * can be set globally using the `Set Selenium Timeout keyword`.
205 | *
206 | * All timeouts can be given as numbers considered seconds (e.g. 0.5 or 42) or 207 | * 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.
211 | *
212 | * Log Level
213 | * There are several keywords that take timeout as an argument. All of 214 | * these timeout arguments are optional. The default is usually INFO.
215 | *
216 | * List of log levels: 217 | * 218 | * 219 | * 220 | * 221 | * 222 | * 223 | * 224 | * 225 | * 226 | * 227 | * 228 | * 229 | * 230 | * 231 | * 232 | * 233 | * 234 | * 235 | * 236 | * 237 | * 238 | * 239 | * 240 | * 241 | * 242 | *
Log LevelDescription
DEBUG
INFO
HTMLSame as INFO, but message is in HTML format
TRACE
WARN
243 | */ 244 | public class Selenium2Library extends AnnotationLibrary { 245 | 246 | /** 247 | * The list of keyword patterns for the AnnotationLibrary 248 | */ 249 | public static final String KEYWORD_PATTERN = "com/github/markusbernhardt/selenium2library/keywords/**/*.class"; 250 | 251 | /** 252 | * The javadoc to libdoc converter 253 | */ 254 | public static final Javadoc2Libdoc JAVADOC_2_LIBDOC = new Javadoc2Libdoc(Selenium2Library.class); 255 | 256 | /** 257 | * The library documentation is written in HTML 258 | */ 259 | public static final String ROBOT_LIBRARY_DOC_FORMAT = "HTML"; 260 | 261 | /** 262 | * The scope of this library is global. 263 | */ 264 | public static final String ROBOT_LIBRARY_SCOPE = "GLOBAL"; 265 | 266 | /** 267 | * The actual version of this library. Loaded from maven project. 268 | */ 269 | public static final String ROBOT_LIBRARY_VERSION = loadRobotLibraryVersion(); 270 | 271 | private static String loadRobotLibraryVersion() { 272 | try { 273 | return ResourceBundle.getBundle(Selenium2Library.class.getCanonicalName().replace(".", File.separator)) 274 | .getString("version"); 275 | } catch (RuntimeException e) { 276 | return "unknown"; 277 | } 278 | } 279 | 280 | public Selenium2Library() { 281 | this("5.0"); 282 | } 283 | 284 | public Selenium2Library(String timeout) { 285 | this(timeout, "0.0"); 286 | } 287 | 288 | public Selenium2Library(String timeout, String implicitWait) { 289 | this(timeout, implicitWait, "Capture Page Screenshot"); 290 | } 291 | 292 | /** 293 | * Selenium2Library can be imported with optional arguments.
294 | *
295 | * timeout is the default timeout used to wait for all waiting 296 | * actions. It can be changed later with `Set Selenium Timeout`.
297 | *
298 | * implicitWait is the implicit timeout that Selenium waits, when 299 | * looking for elements. It can be changed later with `Set Selenium Implicit 300 | * Wait`. See WebDriver: Advanced Usage of the SeleniumHQ documentation for 303 | * details about WebDriver's implicit wait functionality.
304 | *
305 | * runOnFailure specifies the name of a keyword (from any available 306 | * libraries) to execute when a Selenium2Library keyword fails. By default 307 | * `Capture Page Screenshot` will be used to take a screenshot of the 308 | * current page. Using the value \"Nothing\" will disable this feature 309 | * altogether. See `Register Keyword To Run On Failure` keyword for details 310 | * about this functionality.
311 | *
312 | * Examples: 313 | * 314 | * 315 | * 316 | * 317 | * 318 | * 319 | * 320 | * 321 | * 322 | * 323 | * 324 | * 325 | * 326 | * 327 | * 328 | * 329 | * 330 | * 331 | * 332 | * 333 | * 334 | * 335 | * 336 | * 337 | * 338 | * 339 | * 340 | * 341 | * 342 | * 343 | * 344 | * 346 | * 347 | * 348 | * 349 | * 350 | * 351 | * 352 | * 353 | * 355 | * 356 | *
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 345 | * Source` on failure
LibrarySelenium2Library05Nothing# Sets timeout to 0 seconds, implicitWait to 5 seconds and does 354 | * nothing on failure
357 | * 358 | * @param timeout 359 | * Default=5.0. Optional custom timeout. 360 | * @param implicitWait 361 | * Default=0.0. Optional custom implicit wait time. 362 | * @param keywordToRunOnFailure 363 | * Default=Capture Page Screenshot. Optional custom keyword to 364 | * run on failure. 365 | */ 366 | public Selenium2Library(String timeout, String implicitWait, String keywordToRunOnFailure) { 367 | super(); 368 | addKeywordPattern(KEYWORD_PATTERN); 369 | createKeywordFactory(); // => init annotations 370 | browserManagement.setSeleniumTimeout(timeout); 371 | browserManagement.setSeleniumImplicitWait(implicitWait); 372 | runOnFailure.registerKeywordToRunOnFailure(keywordToRunOnFailure); 373 | } 374 | 375 | // ############################## 376 | // Autowired References 377 | // ############################## 378 | 379 | /** 380 | * Instantiated BrowserManagement keyword bean 381 | */ 382 | @Autowired 383 | protected BrowserManagement browserManagement; 384 | 385 | /** 386 | * Instantiated Cookie keyword bean 387 | */ 388 | @Autowired 389 | protected Cookie cookie; 390 | 391 | /** 392 | * Instantiated Element keyword bean 393 | */ 394 | @Autowired 395 | protected Element element; 396 | 397 | /** 398 | * Instantiated FormElement keyword bean 399 | */ 400 | @Autowired 401 | protected FormElement formElement; 402 | 403 | /** 404 | * Instantiated JavaScript keyword bean 405 | */ 406 | @Autowired 407 | protected JavaScript javaScript; 408 | 409 | /** 410 | * Instantiated Logging keyword bean 411 | */ 412 | @Autowired 413 | protected Logging logging; 414 | 415 | /** 416 | * Instantiated RunOnFailure keyword bean 417 | */ 418 | @Autowired 419 | protected RunOnFailure runOnFailure; 420 | 421 | /** 422 | * Instantiated Screenshot keyword bean 423 | */ 424 | @Autowired 425 | protected Screenshot screenshot; 426 | 427 | /** 428 | * Instantiated SelectElement keyword bean 429 | */ 430 | @Autowired 431 | protected SelectElement selectElement; 432 | 433 | /** 434 | * Instantiated TableElement keyword bean 435 | */ 436 | @Autowired 437 | protected TableElement tableElement; 438 | 439 | /** 440 | * Instantiated Waiting keyword bean 441 | */ 442 | @Autowired 443 | protected Waiting waiting; 444 | 445 | // ############################## 446 | // Getter / Setter 447 | // ############################## 448 | 449 | public BrowserManagement getBrowserManagement() { 450 | return browserManagement; 451 | } 452 | 453 | public Cookie getCookie() { 454 | return cookie; 455 | } 456 | 457 | public Element getElement() { 458 | return element; 459 | } 460 | 461 | public FormElement getFormElement() { 462 | return formElement; 463 | } 464 | 465 | public JavaScript getJavaScript() { 466 | return javaScript; 467 | } 468 | 469 | public Logging getLogging() { 470 | return logging; 471 | } 472 | 473 | public RunOnFailure getRunOnFailure() { 474 | return runOnFailure; 475 | } 476 | 477 | public Screenshot getScreenshot() { 478 | return screenshot; 479 | } 480 | 481 | public SelectElement getSelectElement() { 482 | return selectElement; 483 | } 484 | 485 | public TableElement getTableElement() { 486 | return tableElement; 487 | } 488 | 489 | public Waiting getWaiting() { 490 | return waiting; 491 | } 492 | 493 | // ############################## 494 | // Public Methods 495 | // ############################## 496 | 497 | @Override 498 | public Object runKeyword(String keywordName, Object[] args) { 499 | return super.runKeyword(keywordName, toStrings(args)); 500 | } 501 | 502 | @Override 503 | public String getKeywordDocumentation(String keywordName) { 504 | String keywordDocumentation = JAVADOC_2_LIBDOC.getKeywordDocumentation(keywordName); 505 | if (keywordDocumentation == null) { 506 | try { 507 | return super.getKeywordDocumentation(keywordName); 508 | } catch (NullPointerException e) { 509 | return ""; 510 | } 511 | } 512 | return keywordDocumentation; 513 | } 514 | 515 | /** 516 | * Returns the currently active Selenium2Library instance. 517 | * 518 | * @return the library instance 519 | * @throws ScriptException - if error occurs in script 520 | */ 521 | public static Selenium2Library getLibraryInstance() throws ScriptException { 522 | ScriptEngine engine = new ScriptEngineManager().getEngineByName("python"); 523 | engine.put("library", "Selenium2Library"); 524 | engine.eval("from robot.libraries.BuiltIn import BuiltIn"); 525 | engine.eval("instance = BuiltIn().get_library_instance(library)"); 526 | return (Selenium2Library) engine.get("instance"); 527 | } 528 | 529 | // ############################## 530 | // Internal Methods 531 | // ############################## 532 | 533 | /** 534 | * Convert all arguments in the object array to string 535 | * 536 | * @param args 537 | * array with object to convert to the return string 538 | * @return the return string 539 | */ 540 | protected Object[] toStrings(Object[] args) { 541 | Object[] newArgs = new Object[args.length]; 542 | for (int i = 0; i < newArgs.length; i++) { 543 | if (args[i] == null || args[i].getClass().isArray()) { 544 | newArgs[i] = args[i]; 545 | } else { 546 | newArgs[i] = args[i].toString(); 547 | } 548 | } 549 | return newArgs; 550 | } 551 | } 552 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/Selenium2LibraryFatalException.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library; 2 | 3 | /** 4 | * A raised exception of this type ends all test executions. 5 | */ 6 | @SuppressWarnings("serial") 7 | public class Selenium2LibraryFatalException extends RuntimeException { 8 | 9 | /** 10 | * Mark this exception as fatal 11 | */ 12 | public static final boolean ROBOT_EXIT_ON_FAILURE = true; 13 | 14 | public Selenium2LibraryFatalException() { 15 | super(); 16 | } 17 | 18 | public Selenium2LibraryFatalException(String string) { 19 | super(string); 20 | } 21 | 22 | public Selenium2LibraryFatalException(Throwable t) { 23 | super(t); 24 | } 25 | 26 | public Selenium2LibraryFatalException(String string, Throwable t) { 27 | super(string, t); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/Selenium2LibraryNonFatalException.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library; 2 | 3 | /** 4 | * A raised exception of this type marks the step as failed, but does not end 5 | * all test executions. 6 | */ 7 | @SuppressWarnings("serial") 8 | public class Selenium2LibraryNonFatalException extends RuntimeException { 9 | 10 | /** 11 | * Mark this exception as non fatal 12 | */ 13 | public static final boolean ROBOT_EXIT_ON_FAILURE = false; 14 | 15 | public Selenium2LibraryNonFatalException() { 16 | super(); 17 | } 18 | 19 | public Selenium2LibraryNonFatalException(String string) { 20 | super(string); 21 | } 22 | 23 | public Selenium2LibraryNonFatalException(Throwable t) { 24 | super(t); 25 | } 26 | 27 | public Selenium2LibraryNonFatalException(String string, Throwable t) { 28 | super(string, t); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/keywords/Cookie.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.keywords; 2 | 3 | import java.util.ArrayList; 4 | 5 | import org.robotframework.javalib.annotation.ArgumentNames; 6 | import org.robotframework.javalib.annotation.Autowired; 7 | import org.robotframework.javalib.annotation.RobotKeyword; 8 | import org.robotframework.javalib.annotation.RobotKeywordOverload; 9 | import org.robotframework.javalib.annotation.RobotKeywords; 10 | 11 | import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; 12 | import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; 13 | 14 | @RobotKeywords 15 | public class Cookie extends RunOnFailureKeywordsAdapter { 16 | 17 | /** 18 | * Instantiated BrowserManagement keyword bean 19 | */ 20 | @Autowired 21 | protected BrowserManagement browserManagement; 22 | 23 | // ############################## 24 | // Keywords 25 | // ############################## 26 | 27 | /** 28 | * Deletes all cookies.
29 | */ 30 | @RobotKeyword 31 | public void deleteAllCookies() { 32 | browserManagement.getCurrentWebDriver().manage().deleteAllCookies(); 33 | } 34 | 35 | /** 36 | * Deletes cookie matching name.
37 | *
38 | * If the cookie is not found, nothing happens
39 | * 40 | * @param name 41 | * The name of the cookie to delete. 42 | */ 43 | @RobotKeyword 44 | @ArgumentNames({ "name" }) 45 | public void deleteCookie(String name) { 46 | browserManagement.getCurrentWebDriver().manage().deleteCookieNamed(name); 47 | } 48 | 49 | /** 50 | * Returns all cookies of the current page.
51 | * 52 | * @return All cookies of the current page. 53 | */ 54 | @RobotKeyword 55 | public String getCookies() { 56 | StringBuffer ret = new StringBuffer(); 57 | 58 | ArrayList cookies = new ArrayList(browserManagement 59 | .getCurrentWebDriver().manage().getCookies()); 60 | for (int i = 0; i < cookies.size(); i++) { 61 | ret.append(cookies.get(i).getName() + "=" + cookies.get(i).getValue()); 62 | if (i != cookies.size() - 1) { 63 | ret.append("; "); 64 | } 65 | } 66 | 67 | return ret.toString(); 68 | } 69 | 70 | /** 71 | * Returns value of cookie found with name.
72 | *
73 | * If no cookie is found with name, this keyword fails.
74 | * 75 | * @param name 76 | * The name of the cookie 77 | * @return The value of the found cookie 78 | */ 79 | @RobotKeyword 80 | @ArgumentNames({ "name" }) 81 | public String getCookieValue(String name) { 82 | org.openqa.selenium.Cookie cookie = browserManagement.getCurrentWebDriver().manage().getCookieNamed(name); 83 | 84 | if (cookie != null) { 85 | return cookie.getValue(); 86 | } else { 87 | throw new Selenium2LibraryNonFatalException(String.format("Cookie with name %s not found.", name)); 88 | } 89 | } 90 | 91 | @RobotKeywordOverload 92 | public void addCookie(String name, String value) { 93 | addCookie(name, value, null); 94 | } 95 | 96 | @RobotKeywordOverload 97 | public void addCookie(String name, String value, String path) { 98 | addCookie(name, value, path, null); 99 | } 100 | 101 | @RobotKeywordOverload 102 | public void addCookie(String name, String value, String path, String domain) { 103 | addCookie(name, value, path, domain, ""); 104 | } 105 | 106 | @RobotKeywordOverload 107 | public void addCookie(String name, String value, String path, String domain, String secure) { 108 | addCookie(name, value, path, domain, secure, null); 109 | } 110 | 111 | /** 112 | * Adds a cookie to your current session.
113 | * 114 | * @param name 115 | * The name of the cookie. 116 | * @param value 117 | * The cookie value. 118 | * @param path 119 | * Default=NONE. The path the cookie is visible to. 120 | * @param domain 121 | * Default=NONE. The domain the cookie is visible to. 122 | * @param secure 123 | * Default=NONE. Whether this cookie requires a secure 124 | * connection. 125 | * @param expiry 126 | * Default=NONE. The cookie's expiration date 127 | */ 128 | @RobotKeyword 129 | @ArgumentNames({ "name", "value", "path=NONE", "domain=NONE", "secure=NONE", "expiry=NONE" }) 130 | public void addCookie(String name, String value, String path, String domain, String secure, String expiry) { 131 | // Parameter expiry not used by Python library 132 | org.openqa.selenium.Cookie cookie = new org.openqa.selenium.Cookie(name, value, domain, path, null, 133 | "true".equals(secure.toLowerCase())); 134 | browserManagement.getCurrentWebDriver().manage().addCookie(cookie); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/keywords/JavaScript.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.keywords; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.nio.MappedByteBuffer; 7 | import java.nio.channels.FileChannel; 8 | import java.nio.charset.Charset; 9 | import java.util.Arrays; 10 | 11 | import org.openqa.selenium.Alert; 12 | import org.openqa.selenium.JavascriptExecutor; 13 | import org.openqa.selenium.WebDriverException; 14 | import org.robotframework.javalib.annotation.ArgumentNames; 15 | import org.robotframework.javalib.annotation.Autowired; 16 | import org.robotframework.javalib.annotation.RobotKeyword; 17 | import org.robotframework.javalib.annotation.RobotKeywordOverload; 18 | import org.robotframework.javalib.annotation.RobotKeywords; 19 | 20 | import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; 21 | import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; 22 | import com.github.markusbernhardt.selenium2library.utils.Python; 23 | 24 | @RobotKeywords 25 | public class JavaScript extends RunOnFailureKeywordsAdapter { 26 | 27 | protected boolean acceptOnNextConfirmationDefault = true; 28 | protected boolean acceptOnNextConfirmation = true; 29 | 30 | /** 31 | * Instantiated BrowserManagement keyword bean 32 | */ 33 | @Autowired 34 | protected BrowserManagement browserManagement; 35 | 36 | /** 37 | * Instantiated Logging keyword bean 38 | */ 39 | @Autowired 40 | protected Logging logging; 41 | 42 | // ############################## 43 | // Keywords 44 | // ############################## 45 | 46 | @RobotKeywordOverload 47 | public void alertShouldBePresent() { 48 | alertShouldBePresent(""); 49 | } 50 | 51 | /** 52 | * Verify an alert is present and dismiss it.
53 | *
54 | * If text is a non-empty string, then it is also verified that the 55 | * message of the alert equals to text.
56 | *
57 | * Will fail if no alert is present. Note that following keywords will fail 58 | * unless the alert is confirmed by this keyword or another like `Confirm 59 | * Action`.
60 | * 61 | * @param text 62 | * Default=NONE. The alert message to verify. 63 | */ 64 | @RobotKeyword 65 | @ArgumentNames({ "text=NONE" }) 66 | public void alertShouldBePresent(String text) { 67 | String alertText = confirmAction(); 68 | if (text != null && !alertText.equals(text)) { 69 | throw new Selenium2LibraryNonFatalException(String.format("Alert text should have been '%s' but was '%s'", 70 | text, alertText)); 71 | } 72 | } 73 | 74 | /** 75 | * Cancel will be selected the next time a confirmation dialog appears.
76 | *
77 | * Note that every time a confirmation comes up, it must be confirmed by the 78 | * keywords 'Alert Should Be Present' or `Confirm Action`. Otherwise all 79 | * following operations will fail.
80 | */ 81 | @RobotKeyword 82 | public void chooseCancelOnNextConfirmation() { 83 | acceptOnNextConfirmation = false; 84 | } 85 | 86 | /** 87 | * Ok will be selected the next time a confirmation dialog appears.
88 | *
89 | * Note that every time a confirmation comes up, it must be confirmed by the 90 | * keywords 'Alert Should Be Present' or `Confirm Action`. Otherwise all 91 | * following operations will fail.
92 | */ 93 | @RobotKeyword 94 | public void chooseOkOnNextConfirmation() { 95 | acceptOnNextConfirmation = true; 96 | } 97 | 98 | /** 99 | * Cancel will as default be selected from now on every time a confirmation 100 | * dialog appears.
101 | *
102 | * Note that every time a confirmation comes up, it must be confirmed by the 103 | * keywords 'Alert Should Be Present' or `Confirm Action`. Otherwise all 104 | * following operations will fail.
105 | */ 106 | @RobotKeyword 107 | public void chooseCancelOnConfirmation() { 108 | acceptOnNextConfirmationDefault = false; 109 | acceptOnNextConfirmation = false; 110 | } 111 | 112 | /** 113 | * Ok will as default be selected from now on every time a confirmation 114 | * dialog appears.
115 | *
116 | * Note that every time a confirmation comes up, it must be confirmed by the 117 | * keywords 'Alert Should Be Present' or `Confirm Action`. Otherwise all 118 | * following operations will fail.
119 | */ 120 | @RobotKeyword 121 | public void chooseOkOnConfirmation() { 122 | acceptOnNextConfirmationDefault = true; 123 | acceptOnNextConfirmation = true; 124 | } 125 | 126 | /** 127 | * Dismisses currently shown confirmation dialog and returns its message.
128 | *
129 | * By default, this keyword chooses 'OK' option from the dialog. If 'Cancel' 130 | * needs to be chosen, keyword `Choose Cancel On Next Confirmation` must be 131 | * called before the action that causes the confirmation dialog to be shown.
132 | *
133 | * Example: 134 | * 135 | * 136 | * 137 | * 138 | * 139 | * 140 | * 141 | * 142 | * 143 | * 144 | * 145 | * 146 | * 147 | * 148 | * 149 | * 150 | * 151 | * 152 | * 153 | * 154 | * 155 | * 156 | * 157 | * 158 | * 159 | * 160 | * 161 | * 162 | * 163 | * 164 | * 165 | * 166 | * 167 | * 168 | * 169 | * 170 | * 171 | *
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
172 | * 173 | * @return The dialog message. 174 | */ 175 | @RobotKeyword 176 | public String confirmAction() { 177 | try { 178 | Alert alert = browserManagement.getCurrentWebDriver().switchTo().alert(); 179 | String text = alert.getText().replace("\n", ""); 180 | if (acceptOnNextConfirmation) { 181 | alert.accept(); 182 | } else { 183 | alert.dismiss(); 184 | } 185 | acceptOnNextConfirmation = acceptOnNextConfirmationDefault; 186 | return text; 187 | } catch (WebDriverException wde) { 188 | throw new Selenium2LibraryNonFatalException("There were no alerts"); 189 | } 190 | } 191 | 192 | /** 193 | * Execute the given JavaScript code.
194 | *
195 | * The given code may contain multiple lines of code, but must contain a 196 | * return statement (with the value to be returned) at the end.
197 | *
198 | * The given code may be divided into multiple cells in the test data. In 199 | * that case, the parts are concatenated together without adding spaces. If 200 | * the given code is an absolute path to an existing file, the JavaScript to 201 | * execute will be read from that file. Forward slashes work as a path 202 | * separator on all operating systems.
203 | *
204 | * Note that by default the code will be executed in the context of the 205 | * Selenium object itself, so this will refer to the Selenium object. 206 | * Use window to refer to the window of your application, e.g. 207 | * window.document.getElementById('foo').
208 | *
209 | * Example: 210 | * 211 | * 212 | * 213 | * 214 | * 215 | * 216 | * 217 | * 218 | * 219 | * 220 | * 221 | *
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
222 | * 223 | * @param code 224 | * The JavaScript code or a file name. 225 | * @return The return value of the executed code. 226 | */ 227 | @RobotKeyword 228 | @ArgumentNames({ "*code" }) 229 | public Object executeJavascript(String... code) { 230 | String js = getJavascriptToExecute(Python.join("", Arrays.asList(code))); 231 | String.format("Executing JavaScript:\n%s", js); 232 | return ((JavascriptExecutor) browserManagement.getCurrentWebDriver()).executeScript(js); 233 | } 234 | 235 | /** 236 | * Execute the given JavaScript code asynchronously.
237 | *
238 | * The given code may contain multiple lines of code, but must contain a 239 | * return statement (with the value to be returned) at the end.
240 | *
241 | * The given code may be divided into multiple cells in the test data. In 242 | * that case, the parts are concatenated together without adding spaces. If 243 | * the given code is an absolute path to an existing file, the JavaScript to 244 | * execute will be read from that file. Forward slashes work as a path 245 | * separator on all operating systems.
246 | *
247 | * Note that by default the code will be executed in the context of the 248 | * Selenium object itself, so this will refer to the Selenium object. 249 | * Use window to refer to the window of your application, e.g. 250 | * window.document.getElementById('foo').
251 | *
252 | * Example: 253 | * 254 | * 255 | * 256 | * 257 | * 258 | * 259 | * 260 | * 261 | * 262 | * 263 | * 264 | *
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
265 | * 266 | * @param code 267 | * The JavaScript code or a file name. 268 | * @return The return value of the executed code. 269 | */ 270 | @RobotKeyword 271 | @ArgumentNames({ "*code" }) 272 | public Object executeAsyncJavascript(String... code) { 273 | String js = getJavascriptToExecute(Python.join("", Arrays.asList(code))); 274 | String.format("Executing JavaScript:\n%s", js); 275 | return ((JavascriptExecutor) browserManagement.getCurrentWebDriver()).executeAsyncScript(js); 276 | } 277 | 278 | /** 279 | * Returns the text of current JavaScript alert.
280 | *
281 | * This keyword will fail if no alert is present. Note that following 282 | * keywords will fail unless the alert is confirmed by the keywords 'Alert 283 | * Should Be Present' or `Confirm Action`. 284 | * 285 | * @return The alert message. 286 | */ 287 | @RobotKeyword 288 | public String getAlertMessage() { 289 | try { 290 | Alert alert = browserManagement.getCurrentWebDriver().switchTo().alert(); 291 | String text = alert.getText().replace("\n", ""); 292 | return text; 293 | } catch (WebDriverException wde) { 294 | throw new Selenium2LibraryNonFatalException("There were no alerts"); 295 | } 296 | } 297 | 298 | // ############################## 299 | // Internal Methods 300 | // ############################## 301 | 302 | protected static String readFile(String path) throws IOException { 303 | FileInputStream stream = new FileInputStream(new File(path)); 304 | try { 305 | FileChannel fc = stream.getChannel(); 306 | MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); 307 | /* Instead of using default, pass in a decoder. */ 308 | return Charset.defaultCharset().decode(bb).toString(); 309 | } finally { 310 | stream.close(); 311 | } 312 | } 313 | 314 | protected String getJavascriptToExecute(String code) { 315 | String codepath = code.replace('/', File.separatorChar); 316 | if (!new File(codepath).isFile()) { 317 | return code; 318 | } 319 | logging.html(String.format("Reading JavaScript from file %s.", 320 | codepath.replace(File.separatorChar, '/'), codepath)); 321 | try { 322 | return readFile(codepath); 323 | } catch (IOException e) { 324 | throw new Selenium2LibraryNonFatalException("Cannot read JavaScript file: " + codepath); 325 | } 326 | } 327 | 328 | } 329 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/keywords/Logging.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.keywords; 2 | 3 | import java.io.File; 4 | import java.io.FileWriter; 5 | import java.io.IOException; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import org.python.core.PyString; 11 | import org.python.util.PythonInterpreter; 12 | import org.robotframework.javalib.annotation.ArgumentNames; 13 | import org.robotframework.javalib.annotation.Autowired; 14 | import org.robotframework.javalib.annotation.RobotKeyword; 15 | import org.robotframework.javalib.annotation.RobotKeywordOverload; 16 | import org.robotframework.javalib.annotation.RobotKeywords; 17 | 18 | import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; 19 | import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; 20 | 21 | @RobotKeywords 22 | public class Logging extends RunOnFailureKeywordsAdapter { 23 | 24 | protected final static Map VALID_LOG_LEVELS; 25 | protected static String logDir = null; 26 | 27 | static { 28 | VALID_LOG_LEVELS = new HashMap(); 29 | VALID_LOG_LEVELS.put("debug", new String[] { "debug", "" }); 30 | VALID_LOG_LEVELS.put("html", new String[] { "info", ", True, False" }); 31 | VALID_LOG_LEVELS.put("info", new String[] { "info", "" }); 32 | VALID_LOG_LEVELS.put("trace", new String[] { "trace", "" }); 33 | VALID_LOG_LEVELS.put("warn", new String[] { "warn", "" }); 34 | } 35 | 36 | /** 37 | * Instantiated BrowserManagement keyword bean 38 | */ 39 | @Autowired 40 | protected BrowserManagement browserManagement; 41 | 42 | // ############################## 43 | // Keywords 44 | // ############################## 45 | 46 | @RobotKeywordOverload 47 | public List logWindowIdentifiers() { 48 | return logWindowIdentifiers("INFO"); 49 | } 50 | 51 | /** 52 | * Logs and returns the id attributes of all windows known to the current 53 | * browser instance.
54 | * 55 | * @param logLevel 56 | * Default=INFO. Optional log level. 57 | * @return List of window id attributes. 58 | * 59 | * @see BrowserManagement#getWindowIdentifiers 60 | */ 61 | @RobotKeyword 62 | @ArgumentNames({ "logLevel=INFO" }) 63 | public List logWindowIdentifiers(String logLevel) { 64 | List windowIdentifiers = browserManagement.getWindowIdentifiers(); 65 | for (String windowIdentifier : windowIdentifiers) { 66 | log(windowIdentifier, logLevel); 67 | } 68 | return windowIdentifiers; 69 | } 70 | 71 | @RobotKeywordOverload 72 | public List logWindowNames() { 73 | return logWindowNames("INFO"); 74 | } 75 | 76 | /** 77 | * Logs and returns the names of all windows known to the current browser 78 | * instance.
79 | *
80 | * See `Introduction` for details about the logLevel.
81 | * 82 | * @param logLevel 83 | * Default=INFO. Optional log level. 84 | * @return List of windows names. 85 | * 86 | * @see BrowserManagement#getWindowNames 87 | */ 88 | @RobotKeyword 89 | @ArgumentNames({ "logLevel=INFO" }) 90 | public List logWindowNames(String logLevel) { 91 | List windowIdentifiers = browserManagement.getWindowNames(); 92 | for (String windowIdentifier : windowIdentifiers) { 93 | log(windowIdentifier, logLevel); 94 | } 95 | return windowIdentifiers; 96 | } 97 | 98 | @RobotKeywordOverload 99 | public List logWindowTitles() { 100 | return logWindowTitles("INFO"); 101 | } 102 | 103 | /** 104 | * Logs and returns the titles of all windows known to the current browser 105 | * instance.
106 | *
107 | * See `Introduction` for details about the logLevel.
108 | * 109 | * @param logLevel 110 | * Default=INFO. Optional log level. 111 | * @return List of window titles. 112 | * 113 | * @see BrowserManagement#getWindowTitles 114 | */ 115 | @RobotKeyword 116 | @ArgumentNames({ "logLevel=INFO" }) 117 | public List logWindowTitles(String logLevel) { 118 | List windowIdentifiers = browserManagement.getWindowTitles(); 119 | for (String windowIdentifier : windowIdentifiers) { 120 | log(windowIdentifier, logLevel); 121 | } 122 | return windowIdentifiers; 123 | } 124 | 125 | @RobotKeywordOverload 126 | public String logLocation() { 127 | return logLocation("INFO"); 128 | } 129 | 130 | /** 131 | * Logs and returns the location of the current browser instance.
132 | *
133 | * See `Introduction` for details about the logLevel.
134 | * 135 | * @param logLevel 136 | * Default=INFO. Optional log level. 137 | * @return The current location. 138 | * 139 | * @see BrowserManagement#getLocation 140 | */ 141 | @RobotKeyword 142 | @ArgumentNames({ "logLevel=INFO" }) 143 | public String logLocation(String logLevel) { 144 | String actual = browserManagement.getLocation(); 145 | log(actual, logLevel); 146 | return actual; 147 | } 148 | 149 | @RobotKeywordOverload 150 | public String logSource() { 151 | return logSource("INFO"); 152 | } 153 | 154 | /** 155 | * Logs and returns the entire html source of the current page or frame.
156 | *
157 | * See `Introduction` for details about the logLevel.
158 | * 159 | * @param logLevel 160 | * Default=INFO. Optional log level. 161 | * @return The entire html source. 162 | * 163 | * @see BrowserManagement#getSource 164 | */ 165 | @RobotKeyword 166 | @ArgumentNames({ "logLevel=INFO" }) 167 | public String logSource(String logLevel) { 168 | String actual = browserManagement.getSource(); 169 | log(actual, logLevel); 170 | return actual; 171 | } 172 | 173 | @RobotKeywordOverload 174 | public String logTitle() { 175 | return logTitle("INFO"); 176 | } 177 | 178 | /** 179 | * Logs and returns the title of current page.
180 | *
181 | * See `Introduction` for details about the logLevel.
182 | * 183 | * @param logLevel 184 | * Default=INFO. Optional log level. 185 | * @return The page title. 186 | * 187 | * @see BrowserManagement#getSource 188 | */ 189 | @RobotKeyword 190 | @ArgumentNames({ "logLevel=INFO" }) 191 | public String logTitle(String logLevel) { 192 | String actual = browserManagement.getTitle(); 193 | log(actual, logLevel); 194 | return actual; 195 | } 196 | 197 | @RobotKeywordOverload 198 | public String logSystemInfo() { 199 | return logSystemInfo("INFO"); 200 | } 201 | 202 | /** 203 | * Logs and returns basic system information about the execution 204 | * environment.
205 | *
206 | * See `Introduction` for details about the logLevel.
207 | * 208 | * @param logLevel 209 | * Default=INFO. Optional log level. 210 | * @return System information. 211 | * 212 | * @see BrowserManagement#getSystemInfo 213 | */ 214 | @RobotKeyword 215 | @ArgumentNames({ "logLevel=INFO" }) 216 | public String logSystemInfo(String logLevel) { 217 | String actual = browserManagement.getSystemInfo(); 218 | log(actual, logLevel); 219 | return actual; 220 | } 221 | 222 | @RobotKeywordOverload 223 | public String logRemoteCapabilities() { 224 | return logRemoteCapabilities("INFO"); 225 | } 226 | 227 | /** 228 | * Logs and returns the actually supported capabilities of the remote 229 | * browser instance.
230 | *
231 | * Not all server implementations will support every WebDriver feature. 232 | * Therefore, the client and server should use JSON objects with the 233 | * properties listed below when describing which features a user requests 234 | * that a session support. If a session cannot support a capability that 235 | * is requested in the desired capabilities, no error is thrown; a 236 | * read-only capabilities object is returned that indicates the capabilities 237 | * the session actually supports. For more information see: DesiredCapabilities
240 | *
241 | * See `Introduction` for details about the logLevel.
242 | * 243 | * @param logLevel 244 | * Default=INFO. Optional log level. 245 | * @return The capabilities of the remote node. 246 | * 247 | * @see BrowserManagement#getRemoteCapabilities 248 | */ 249 | @RobotKeyword 250 | @ArgumentNames({ "logLevel=INFO" }) 251 | public String logRemoteCapabilities(String logLevel) { 252 | String actual = browserManagement.getRemoteCapabilities(); 253 | log(actual, logLevel); 254 | return actual; 255 | } 256 | 257 | @RobotKeywordOverload 258 | public String logRemoteSessionId() { 259 | return logRemoteSessionId("INFO"); 260 | } 261 | 262 | /** 263 | * Logs and returns the session id of the remote browser instance.
264 | *
265 | * See `Introduction` for details about the logLevel.
266 | * 267 | * @param logLevel 268 | * Default=INFO. Optional log level. 269 | * @return The remote session id. 270 | * 271 | * @see BrowserManagement#getRemoteSessionId 272 | */ 273 | @RobotKeyword 274 | @ArgumentNames({ "logLevel=INFO" }) 275 | public String logRemoteSessionId(String logLevel) { 276 | String actual = browserManagement.getRemoteSessionId(); 277 | log(actual, logLevel); 278 | return actual; 279 | } 280 | 281 | /** 282 | * Set the logDirectory, where captured screenshots are stored, to 283 | * some custom path.
284 | *
285 | * Fails, if either the given path does not exist, is no directory or is not 286 | * writable.
287 | * 288 | * @param logDirectory 289 | * The directory to log to. 290 | * @throws Exception 291 | * - if anything goes wrong. 292 | */ 293 | @RobotKeyword 294 | @ArgumentNames({ "logDirectory" }) 295 | public void setLogDirectory(String logDirectory) throws Exception { 296 | File file = new File(logDirectory); 297 | 298 | if (file.exists() && file.isDirectory() && file.canWrite()) { 299 | Logging.setLogDir(file.getAbsolutePath()); 300 | } else { 301 | throw new Exception("Location given as parameter: " + logDirectory 302 | + " must exist and must be a writeable directory!"); 303 | } 304 | } 305 | 306 | // ############################## 307 | // Internal Methods 308 | // ############################## 309 | 310 | protected void trace(String msg) { 311 | log(msg, "trace"); 312 | } 313 | 314 | protected void debug(String msg) { 315 | log(msg, "debug"); 316 | } 317 | 318 | protected void info(String msg) { 319 | log(msg, "info"); 320 | } 321 | 322 | protected void html(String msg) { 323 | log(msg, "html"); 324 | } 325 | 326 | protected void warn(String msg) { 327 | log(msg, "warn"); 328 | } 329 | 330 | protected void log(String msg, String logLevel) { 331 | String[] methodParameters = VALID_LOG_LEVELS.get(logLevel.toLowerCase()); 332 | if (methodParameters != null) { 333 | log0(msg, methodParameters[0], methodParameters[1]); 334 | } else { 335 | throw new Selenium2LibraryNonFatalException(String.format("Given log level %s is invalid.", logLevel)); 336 | } 337 | } 338 | 339 | protected void log0(String msg, String methodName, String methodArguments) { 340 | if (msg.length() > 1024) { 341 | // Message is too large. 342 | // There is a hard limit of 100k in the Jython source code parser 343 | try { 344 | // Write message to temp file 345 | File tempFile = File.createTempFile("Selenium2Library-", ".log"); 346 | tempFile.deleteOnExit(); 347 | FileWriter writer = new FileWriter(tempFile); 348 | writer.write(msg); 349 | writer.close(); 350 | 351 | // Read the message in Python back and log it. 352 | loggingPythonInterpreter.get().exec( 353 | String.format("from __future__ import with_statement\n" + "\n" 354 | + "with open('%s', 'r') as msg_file:\n" + " msg = msg_file.read()\n" 355 | + " logger.%s(msg%s)", tempFile.getAbsolutePath().replace("\\", "\\\\"), methodName, 356 | methodArguments)); 357 | 358 | } catch (IOException e) { 359 | throw new Selenium2LibraryNonFatalException("Error in handling temp file for long log message.", e); 360 | } 361 | } else { 362 | // Message is small enough to get parsed by Jython 363 | loggingPythonInterpreter.get().exec( 364 | String.format("logger.%s('%s'%s)", methodName, msg.replace("\\", "\\\\").replace("'", "\\'") 365 | .replace("\n", "\\n"), methodArguments)); 366 | } 367 | } 368 | 369 | protected File getLogDir() { 370 | 371 | if (logDir == null 372 | && !loggingPythonInterpreter.get().eval("EXECUTION_CONTEXTS.current").toString().equals("None")) { 373 | PyString logDirName = (PyString) loggingPythonInterpreter.get().eval( 374 | "BuiltIn().get_variables()['${LOG FILE}']"); 375 | if (logDirName != null && !(logDirName.asString().toUpperCase().equals("NONE"))) { 376 | return new File(logDirName.asString()).getParentFile(); 377 | } 378 | logDirName = (PyString) loggingPythonInterpreter.get().eval("BuiltIn().get_variables()['${OUTPUTDIR}']"); 379 | return new File(logDirName.asString()).getParentFile(); 380 | } else { 381 | return new File(logDir); 382 | } 383 | } 384 | 385 | public static void setLogDir(String logDirectory) { 386 | logDir = logDirectory; 387 | } 388 | 389 | protected static ThreadLocal loggingPythonInterpreter = new ThreadLocal() { 390 | 391 | @Override 392 | protected PythonInterpreter initialValue() { 393 | PythonInterpreter pythonInterpreter = new PythonInterpreter(); 394 | pythonInterpreter 395 | .exec("from robot.libraries.BuiltIn import BuiltIn; from robot.running.context import EXECUTION_CONTEXTS; from robot.api import logger;"); 396 | return pythonInterpreter; 397 | } 398 | }; 399 | } 400 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/keywords/RunOnFailure.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.keywords; 2 | 3 | import org.python.util.PythonInterpreter; 4 | import org.robotframework.javalib.annotation.ArgumentNames; 5 | import org.robotframework.javalib.annotation.Autowired; 6 | import org.robotframework.javalib.annotation.RobotKeyword; 7 | import org.robotframework.javalib.annotation.RobotKeywords; 8 | 9 | import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; 10 | 11 | @RobotKeywords 12 | public class RunOnFailure extends RunOnFailureKeywordsAdapter { 13 | 14 | /** 15 | * The keyword to run an failure 16 | */ 17 | protected String runOnFailureKeyword = "Capture Page Screenshot"; 18 | 19 | /** 20 | * Only run keyword on failure if true 21 | */ 22 | protected boolean runningOnFailureRoutine; 23 | 24 | /** 25 | * Instantiated Logging keyword bean 26 | */ 27 | @Autowired 28 | protected Logging logging; 29 | 30 | // ############################## 31 | // Keywords 32 | // ############################## 33 | 34 | /** 35 | * Sets the actual and returns the previous keyword to execute when a 36 | * Selenium2Library keyword fails.
37 | *
38 | * The keyword is the name of a keyword (from any available 39 | * libraries) that will be executed, if a Selenium2Library keyword fails. It 40 | * is not possible to use a keyword that requires arguments. Using the value 41 | * Nothing will disable this feature altogether.
42 | *
43 | * The initial keyword to use is set at importing the library and the 44 | * keyword that is used by default is `Capture Page Screenshot`. Taking a 45 | * screenshot when something failed is a very useful feature, but notice 46 | * that it can slow down the execution.
47 | *
48 | * This keyword returns the name of the previously registered failure 49 | * keyword. It can be used to restore the original value later.
50 | *
51 | * Example: 52 | * 53 | * 54 | * 55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | * 62 | * 63 | * 65 | * 66 | * 67 | * 68 | * 69 | * 70 | * 71 | * 72 | *
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 64 | * in a variable.
Register Keyword To Run On Failure${previous kw}# Restore to the previous keyword.
73 | * 74 | * @param keyword 75 | * The keyword to execute on failure 76 | * @return The previous keyword 77 | */ 78 | @RobotKeyword 79 | @ArgumentNames({ "keyword" }) 80 | public String registerKeywordToRunOnFailure(String keyword) { 81 | String oldKeyword = runOnFailureKeyword; 82 | String oldKeywordText = oldKeyword != null ? oldKeyword : "No keyword"; 83 | 84 | String newKeyword = !keyword.trim().toLowerCase().equals("nothing") ? keyword : null; 85 | String newKeywordText = newKeyword != null ? newKeyword : "No keyword"; 86 | 87 | runOnFailureKeyword = newKeyword; 88 | logging.info(String.format("%s will be run on failure.", newKeywordText)); 89 | 90 | return oldKeywordText; 91 | } 92 | 93 | // ############################## 94 | // Internal Methods 95 | // ############################## 96 | 97 | protected static ThreadLocal runOnFailurePythonInterpreter = new ThreadLocal() { 98 | 99 | @Override 100 | protected PythonInterpreter initialValue() { 101 | PythonInterpreter pythonInterpreter = new PythonInterpreter(); 102 | pythonInterpreter.exec("from robot.libraries.BuiltIn import BuiltIn; from robot.running.context import EXECUTION_CONTEXTS; BIN = BuiltIn();"); 103 | return pythonInterpreter; 104 | } 105 | }; 106 | 107 | public void runOnFailure() { 108 | if (runOnFailureKeyword == null) { 109 | return; 110 | } 111 | if (runningOnFailureRoutine) { 112 | return; 113 | } 114 | if(runOnFailurePythonInterpreter.get().eval("EXECUTION_CONTEXTS.current").toString().equals("None")) { 115 | return; 116 | } 117 | 118 | try { 119 | runOnFailurePythonInterpreter.get().exec( 120 | String.format("BIN.run_keyword('%s')", 121 | runOnFailureKeyword.replace("'", "\\'").replace("\n", "\\n"))); 122 | } catch (RuntimeException r) { 123 | logging.warn(String.format("Keyword '%s' could not be run on failure%s", runOnFailureKeyword, 124 | r.getMessage() != null ? " '" + r.getMessage() + "'" : "")); 125 | } finally { 126 | runningOnFailureRoutine = false; 127 | } 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/keywords/Screenshot.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.keywords; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | 7 | import org.openqa.selenium.OutputType; 8 | import org.openqa.selenium.TakesScreenshot; 9 | import org.robotframework.javalib.annotation.ArgumentNames; 10 | import org.robotframework.javalib.annotation.Autowired; 11 | import org.robotframework.javalib.annotation.RobotKeyword; 12 | import org.robotframework.javalib.annotation.RobotKeywordOverload; 13 | import org.robotframework.javalib.annotation.RobotKeywords; 14 | 15 | import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; 16 | import com.github.markusbernhardt.selenium2library.utils.Robotframework; 17 | 18 | @RobotKeywords 19 | public class Screenshot extends RunOnFailureKeywordsAdapter { 20 | 21 | /** 22 | * Instantiated BrowserManagement keyword bean 23 | */ 24 | @Autowired 25 | protected BrowserManagement browserManagement; 26 | 27 | /** 28 | * Instantiated Logging keyword bean 29 | */ 30 | @Autowired 31 | protected Logging logging; 32 | 33 | // ############################## 34 | // Keywords 35 | // ############################## 36 | @RobotKeywordOverload 37 | public void capturePageScreenshot() { 38 | capturePageScreenshot(null); 39 | } 40 | 41 | /** 42 | * Take a screenshot of the current page and embed it into the log.
43 | *
44 | * The filename argument specifies the name of the file to write the 45 | * screenshot into. If no filename is given, the screenshot is saved into 46 | * file selenium-screenshot-<counter>.png under the directory where 47 | * the Robot Framework log file is written into. The filename is also 48 | * considered relative to the same directory, if it is not given in absolute 49 | * format.
50 | *
51 | * A CSS can be used to modify how the screenshot is taken. By default the 52 | * background color is changed to avoid possible problems with background 53 | * leaking when the page layout is somehow broken.
54 | * 55 | * @param filename 56 | * Default=NONE. Name of the file to write. 57 | */ 58 | @RobotKeyword 59 | @ArgumentNames({ "filename=NONE" }) 60 | public void capturePageScreenshot(String filename) { 61 | File logdir = logging.getLogDir(); 62 | File path = new File(logdir, normalizeFilename(filename)); 63 | String link = Robotframework.getLinkPath(path, logdir); 64 | 65 | TakesScreenshot takesScreenshot = ((TakesScreenshot) browserManagement.getCurrentWebDriver()); 66 | if (takesScreenshot == null) { 67 | logging.warn("Can't take screenshot. No open browser found"); 68 | return; 69 | } 70 | 71 | byte[] png = takesScreenshot.getScreenshotAs(OutputType.BYTES); 72 | writeScreenshot(path, png); 73 | 74 | logging.html(String.format( 75 | "", link, link)); 76 | } 77 | 78 | // ############################## 79 | // Internal Methods 80 | // ############################## 81 | 82 | protected int screenshotIndex = 0; 83 | 84 | protected void writeScreenshot(File path, byte[] png) { 85 | FileOutputStream fos = null; 86 | try { 87 | fos = new FileOutputStream(path); 88 | fos.write(png); 89 | fos.flush(); 90 | } catch (IOException e) { 91 | logging.warn(String.format("Can't write screenshot '%s'", path.getAbsolutePath())); 92 | } finally { 93 | if (fos != null) { 94 | try { 95 | fos.close(); 96 | } catch (IOException e) { 97 | logging.warn("Can't even close stream"); 98 | } 99 | } 100 | } 101 | } 102 | 103 | protected String normalizeFilename(String filename) { 104 | if (filename == null) { 105 | screenshotIndex++; 106 | filename = String.format("selenium-screenshot-%d.png", screenshotIndex); 107 | } else { 108 | filename = filename.replace('/', File.separatorChar); 109 | } 110 | return filename; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/keywords/SelectElement.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.keywords; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.openqa.selenium.NoSuchElementException; 7 | import org.openqa.selenium.WebElement; 8 | import org.openqa.selenium.support.ui.Select; 9 | import org.robotframework.javalib.annotation.ArgumentNames; 10 | import org.robotframework.javalib.annotation.Autowired; 11 | import org.robotframework.javalib.annotation.RobotKeyword; 12 | import org.robotframework.javalib.annotation.RobotKeywordOverload; 13 | import org.robotframework.javalib.annotation.RobotKeywords; 14 | 15 | import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; 16 | import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; 17 | import com.github.markusbernhardt.selenium2library.utils.Python; 18 | 19 | @RobotKeywords 20 | public class SelectElement extends RunOnFailureKeywordsAdapter { 21 | 22 | /** 23 | * Instantiated Element keyword bean 24 | */ 25 | @Autowired 26 | protected Element element; 27 | 28 | /** 29 | * Instantiated Logging keyword bean 30 | */ 31 | @Autowired 32 | protected Logging logging; 33 | 34 | // ############################## 35 | // Keywords 36 | // ############################## 37 | 38 | /** 39 | * Returns the values in the select list identified by locator.
40 | *
41 | * Select list keywords work on both lists and combo boxes. Key attributes 42 | * for select lists are id and name. See `Introduction` for details about 43 | * locators.
44 | * 45 | * @param locator 46 | * The locator to locate the select list. 47 | * @return The select list values 48 | */ 49 | @RobotKeyword 50 | @ArgumentNames({ "locator" }) 51 | public List getListItems(String locator) { 52 | List options = getSelectListOptions(locator); 53 | 54 | return getLabelsForOptions(options); 55 | } 56 | 57 | /** 58 | * Returns the visible label of the first selected element from the select 59 | * list identified by locator.
60 | *
61 | * Select list keywords work on both lists and combo boxes. Key attributes 62 | * for select lists are id and name. See `Introduction` for details about 63 | * locators.
64 | * 65 | * @param locator 66 | * The locator to locate the select list. 67 | * @return The first visible select list label 68 | */ 69 | @RobotKeyword 70 | @ArgumentNames({ "locator" }) 71 | public String getSelectedListLabel(String locator) { 72 | Select select = getSelectList(locator); 73 | 74 | return select.getFirstSelectedOption().getText(); 75 | } 76 | 77 | /** 78 | * Returns the visible labels of the first selected elements as a list from 79 | * the select list identified by locator.
80 | *
81 | * Fails if there is no selection.
82 | *
83 | * Select list keywords work on both lists and combo boxes. Key attributes 84 | * for select lists are id and name. See `Introduction` for details about 85 | * locators.
86 | * 87 | * @param locator 88 | * The locator to locate the select list. 89 | * @return The list of visible select list labels 90 | */ 91 | @RobotKeyword 92 | @ArgumentNames({ "locator" }) 93 | public List getSelectedListLabels(String locator) { 94 | List options = getSelectListOptionsSelected(locator); 95 | 96 | if (options.size() == 0) { 97 | throw new Selenium2LibraryNonFatalException(String.format( 98 | "Select list with locator '%s' does not have any selected values.", locator)); 99 | } 100 | 101 | return getLabelsForOptions(options); 102 | } 103 | 104 | /** 105 | * Returns the value of the first selected element from the select list 106 | * identified by locator.
107 | *
108 | * The return value is read from the value attribute of the selected 109 | * element.
110 | *
111 | * Select list keywords work on both lists and combo boxes. Key attributes 112 | * for select lists are id and name. See `Introduction` for details about 113 | * locators.
114 | * 115 | * @param locator 116 | * The locator to locate the select list. 117 | * @return The first select list value 118 | */ 119 | @RobotKeyword 120 | @ArgumentNames({ "locator" }) 121 | public String getSelectedListValue(String locator) { 122 | Select select = getSelectList(locator); 123 | 124 | return select.getFirstSelectedOption().getAttribute("value"); 125 | } 126 | 127 | /** 128 | * Returns the values of the first selected elements as a list from the 129 | * select list identified by locator.
130 | *
131 | * Fails if there is no selection. The return values are read from the value 132 | * attribute of the selected element.
133 | *
134 | * Select list keywords work on both lists and combo boxes. Key attributes 135 | * for select lists are id and name. See `Introduction` for details about 136 | * locators.
137 | * 138 | * @param locator 139 | * The locator to locate the select list. 140 | * @return The list of select list values 141 | */ 142 | @RobotKeyword 143 | @ArgumentNames({ "locator" }) 144 | public List getSelectedListValues(String locator) { 145 | List options = getSelectListOptionsSelected(locator); 146 | 147 | if (options.size() == 0) { 148 | throw new Selenium2LibraryNonFatalException(String.format( 149 | "Select list with locator '%s' does not have any selected values.", locator)); 150 | } 151 | 152 | return getValuesForOptions(options); 153 | } 154 | 155 | /** 156 | * Verify the selection of the select list identified by locatoris 157 | * exactly *items.
158 | *
159 | * If you want to verify no option is selected, simply give no items.
160 | *
161 | * Select list keywords work on both lists and combo boxes. Key attributes 162 | * for select lists are id and name. See `Introduction` for details about 163 | * locators.
164 | * 165 | * @param locator 166 | * The locator to locate the select list. 167 | * @param items 168 | * The list of items to verify 169 | */ 170 | @RobotKeyword 171 | @ArgumentNames({ "locator", "*items" }) 172 | public void listSelectionShouldBe(String locator, String... items) { 173 | String itemList = items.length != 0 ? String.format("option(s) [ %s ]", Python.join(" | ", items)) 174 | : "no options"; 175 | logging.info(String.format("Verifying list '%s' has %s selected.", locator, itemList)); 176 | 177 | pageShouldContainList(locator); 178 | 179 | List options = getSelectListOptionsSelected(locator); 180 | List selectedLabels = getLabelsForOptions(options); 181 | String message = String.format("List '%s' should have had selection [ %s ] but it was [ %s ].", locator, 182 | Python.join(" | ", items), Python.join(" | ", selectedLabels)); 183 | if (items.length != options.size()) { 184 | throw new Selenium2LibraryNonFatalException(message); 185 | } else { 186 | List selectedValues = getValuesForOptions(options); 187 | 188 | for (String item : items) { 189 | if (!selectedValues.contains(item) && !selectedLabels.contains(item)) { 190 | throw new Selenium2LibraryNonFatalException(message); 191 | } 192 | } 193 | } 194 | } 195 | 196 | /** 197 | * Verify the select list identified by locatorhas no selections.
198 | *
199 | * Select list keywords work on both lists and combo boxes. Key attributes 200 | * for select lists are id and name. See `Introduction` for details about 201 | * locators.
202 | * 203 | * @param locator 204 | * The locator to locate the select list. 205 | */ 206 | @RobotKeyword 207 | @ArgumentNames({ "locator" }) 208 | public void listShouldHaveNoSelections(String locator) { 209 | logging.info(String.format("Verifying list '%s' has no selection.", locator)); 210 | 211 | List options = getSelectListOptionsSelected(locator); 212 | if (!options.equals(null)) { 213 | List selectedLabels = getLabelsForOptions(options); 214 | String items = Python.join(" | ", selectedLabels); 215 | throw new Selenium2LibraryNonFatalException(String.format( 216 | "List '%s' should have had no selection (selection was [ %s ]).", locator, items.toString())); 217 | } 218 | } 219 | 220 | @RobotKeywordOverload 221 | public void pageShouldContainList(String locator) { 222 | pageShouldContainList(locator, ""); 223 | } 224 | 225 | @RobotKeywordOverload 226 | public void pageShouldContainList(String locator, String message) { 227 | pageShouldContainList(locator, message, "INFO"); 228 | } 229 | 230 | /** 231 | * Verify the select list identified by locator is found on the 232 | * current page.
233 | *
234 | * Select list keywords work on both lists and combo boxes. Key attributes 235 | * for select lists are id and name. See `Introduction` for details about 236 | * locators and log levels.
237 | * 238 | * @param locator 239 | * The locator to locate the select list. 240 | * @param message 241 | * Default=NONE. Optional custom error message. 242 | * @param logLevel 243 | * Default=INFO. Optional log level. 244 | */ 245 | @RobotKeyword 246 | @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) 247 | public void pageShouldContainList(String locator, String message, String logLevel) { 248 | element.pageShouldContainElement(locator, "list", message, logLevel); 249 | } 250 | 251 | @RobotKeywordOverload 252 | public void pageShouldNotContainList(String locator) { 253 | pageShouldNotContainList(locator, ""); 254 | } 255 | 256 | @RobotKeywordOverload 257 | public void pageShouldNotContainList(String locator, String message) { 258 | pageShouldNotContainList(locator, message, "INFO"); 259 | } 260 | 261 | /** 262 | * Verify the select list identified by locator is not found on the 263 | * current page.
264 | *
265 | * Select list keywords work on both lists and combo boxes. Key attributes 266 | * for select lists are id and name. See `Introduction` for details about 267 | * locators and log levels.
268 | * 269 | * @param locator 270 | * The locator to locate the select list. 271 | * @param message 272 | * Default=NONE. Optional custom error message. 273 | * @param logLevel 274 | * Default=INFO. Optional log level. 275 | */ 276 | @RobotKeyword 277 | @ArgumentNames({ "locator", "message=NONE", "logLevel=INFO" }) 278 | public void pageShouldNotContainList(String locator, String message, String logLevel) { 279 | element.pageShouldNotContainElement(locator, "list", message, logLevel); 280 | } 281 | 282 | /** 283 | * Select all values of the multi-select list identified by locator.
284 | *
285 | * Select list keywords work on both lists and combo boxes. Key attributes 286 | * for select lists are id and name. See `Introduction` for details about 287 | * locators.
288 | * 289 | * @param locator 290 | * The locator to locate the multi-select list. 291 | */ 292 | @RobotKeyword 293 | @ArgumentNames({ "locator" }) 294 | public void selectAllFromList(String locator) { 295 | logging.info(String.format("Selecting all options from list '%s'.", locator)); 296 | 297 | Select select = getSelectList(locator); 298 | if (!isMultiselectList(select)) { 299 | throw new Selenium2LibraryNonFatalException( 300 | "Keyword 'Select all from list' works only for multiselect lists."); 301 | } 302 | 303 | for (int i = 0; i < select.getOptions().size(); i++) { 304 | select.selectByIndex(i); 305 | } 306 | } 307 | 308 | /** 309 | * Select the given *items of the multi-select list identified by 310 | * locator.
311 | *
312 | * An exception is raised for a single-selection list if the last value does 313 | * not exist in the list and a warning for all other non-existing items. For 314 | * a multi-selection list, an exception is raised for any and all 315 | * non-existing values.
316 | *
317 | * Select list keywords work on both lists and combo boxes. Key attributes 318 | * for select lists are id and name. See `Introduction` for details about 319 | * locators.
320 | * 321 | * @param locator 322 | * The locator to locate the multi-select list. 323 | * @param items 324 | * The list of items to select 325 | */ 326 | @RobotKeyword 327 | @ArgumentNames({ "locator", "*items" }) 328 | public void selectFromList(String locator, String... items) { 329 | String itemList = items.length != 0 ? String.format("option(s) [ %s ]", Python.join(" | ", items)) 330 | : "all options"; 331 | logging.info(String.format("Selecting %s from list '%s'.", itemList, locator)); 332 | 333 | Select select = getSelectList(locator); 334 | 335 | // If no items given, select all values (of in case of single select 336 | // list, go through all values) 337 | if (items.length == 0) { 338 | for (int i = 0; i < select.getOptions().size(); i++) { 339 | select.selectByIndex(i); 340 | } 341 | return; 342 | } 343 | 344 | boolean lastItemFound = false; 345 | List nonExistingItems = new ArrayList(); 346 | for (String item : items) { 347 | lastItemFound = true; 348 | try { 349 | select.selectByValue(item); 350 | } catch (NoSuchElementException e1) { 351 | try { 352 | select.selectByVisibleText(item); 353 | } catch (NoSuchElementException e2) { 354 | nonExistingItems.add(item); 355 | lastItemFound = false; 356 | continue; 357 | } 358 | } 359 | } 360 | 361 | if (nonExistingItems.size() != 0) { 362 | // multi-selection list => throw immediately 363 | if (select.isMultiple()) { 364 | throw new Selenium2LibraryNonFatalException(String.format("Options '%s' not in list '%s'.", 365 | Python.join(", ", nonExistingItems), locator)); 366 | } 367 | 368 | // single-selection list => log warning with not found items 369 | logging.warn(String.format("Option%s '%s' not found within list '%s'.", nonExistingItems.size() == 0 ? "" 370 | : "s", Python.join(", ", nonExistingItems), locator)); 371 | 372 | // single-selection list => throw if last item was not found 373 | if (!lastItemFound) { 374 | throw new Selenium2LibraryNonFatalException(String.format("Option '%s' not in list '%s'.", 375 | nonExistingItems.get(nonExistingItems.size() - 1), locator)); 376 | } 377 | } 378 | } 379 | 380 | /** 381 | * Select the given *indexes of the multi-select list identified by 382 | * locator.
383 | *
384 | * Tries to select by value AND by label. It's generally faster to use 'by 385 | * index/value/label' keywords.
386 | *
387 | * Select list keywords work on both lists and combo boxes. Key attributes 388 | * for select lists are id and name. See `Introduction` for details about 389 | * locators.
390 | * 391 | * @param locator 392 | * The locator to locate the multi-select list. 393 | * @param indexes 394 | * The list of indexes to select 395 | */ 396 | @RobotKeyword 397 | @ArgumentNames({ "locator", "*indexes" }) 398 | public void selectFromListByIndex(String locator, String... indexes) { 399 | if (indexes.length == 0) { 400 | throw new Selenium2LibraryNonFatalException("No index given."); 401 | } 402 | 403 | List tmp = new ArrayList(); 404 | for (String index : indexes) { 405 | tmp.add(index); 406 | } 407 | String items = String.format("index(es) '%s'", Python.join(", ", tmp)); 408 | logging.info(String.format("Selecting %s from list '%s'.", items, locator)); 409 | 410 | Select select = getSelectList(locator); 411 | for (String index : indexes) { 412 | select.selectByIndex(Integer.parseInt(index)); 413 | } 414 | } 415 | 416 | /** 417 | * Select the given *values of the multi-select list identified by 418 | * locator.
419 | *
420 | * Select list keywords work on both lists and combo boxes. Key attributes 421 | * for select lists are id and name. See `Introduction` for details about 422 | * locators.
423 | * 424 | * @param locator 425 | * The locator to locate the multi-select list. 426 | * @param values 427 | * The list of values to select 428 | */ 429 | @RobotKeyword 430 | @ArgumentNames({ "locator", "*values" }) 431 | public void selectFromListByValue(String locator, String... values) { 432 | if (values.length == 0) { 433 | throw new Selenium2LibraryNonFatalException("No value given."); 434 | } 435 | 436 | String items = String.format("value(s) '%s'", Python.join(", ", values)); 437 | logging.info(String.format("Selecting %s from list '%s'.", items, locator)); 438 | 439 | Select select = getSelectList(locator); 440 | for (String value : values) { 441 | select.selectByValue(value); 442 | } 443 | } 444 | 445 | /** 446 | * Select the given *labels of the multi-select list identified by 447 | * locator.
448 | *
449 | * Select list keywords work on both lists and combo boxes. Key attributes 450 | * for select lists are id and name. See `Introduction` for details about 451 | * locators.
452 | * 453 | * @param locator 454 | * The locator to locate the multi-select list. 455 | * @param labels 456 | * The list of labels to select 457 | */ 458 | @RobotKeyword 459 | @ArgumentNames({ "locator", "*labels" }) 460 | public void selectFromListByLabel(String locator, String... labels) { 461 | if (labels.length == 0) { 462 | throw new Selenium2LibraryNonFatalException("No value given."); 463 | } 464 | 465 | String items = String.format("label(s) '%s'", Python.join(", ", labels)); 466 | logging.info(String.format("Selecting %s from list '%s'.", items, locator)); 467 | 468 | Select select = getSelectList(locator); 469 | for (String label : labels) { 470 | select.selectByVisibleText(label); 471 | } 472 | } 473 | 474 | /** 475 | * Unselect the given *items of the multi-select list identified by 476 | * locator.
477 | *
478 | * As a special case, giving an empty *items list will remove all 479 | * selections.
480 | *
481 | * Tries to unselect by value AND by label. It's generally faster to use 'by 482 | * index/value/label' keywords.
483 | *
484 | * Select list keywords work on both lists and combo boxes. Key attributes 485 | * for select lists are id and name. See `Introduction` for details about 486 | * locators.
487 | * 488 | * @param locator 489 | * The locator to locate the multi-select list. 490 | * @param items 491 | * The list of items to select 492 | */ 493 | @RobotKeyword 494 | @ArgumentNames({ "locator", "*items" }) 495 | public void unselectFromList(String locator, String... items) { 496 | String itemList = items.length != 0 ? String.format("option(s) [ %s ]", Python.join(" | ", items)) 497 | : "all options"; 498 | logging.info(String.format("Unselecting %s from list '%s'.", itemList, locator)); 499 | 500 | Select select = getSelectList(locator); 501 | 502 | if (!isMultiselectList(select)) { 503 | throw new Selenium2LibraryNonFatalException( 504 | "Keyword 'Unselect from list' works only for multiselect lists."); 505 | } 506 | 507 | if (items.length == 0) { 508 | select.deselectAll(); 509 | 510 | return; 511 | } 512 | 513 | for (String item : items) { 514 | select.deselectByValue(item); 515 | select.deselectByVisibleText(item); 516 | } 517 | } 518 | 519 | /** 520 | * Unselect the given *indexes of the multi-select list identified by 521 | * locator.
522 | *
523 | * Select list keywords work on both lists and combo boxes. Key attributes 524 | * for select lists are id and name. See `Introduction` for details about 525 | * locators.
526 | * 527 | * @param locator 528 | * The locator to locate the multi-select list. 529 | * @param indexes 530 | * The list of indexes to select 531 | */ 532 | @RobotKeyword 533 | @ArgumentNames({ "locator", "*indexes" }) 534 | public void unselectFromListByIndex(String locator, Integer... indexes) { 535 | if (indexes.equals(null)) { 536 | throw new Selenium2LibraryNonFatalException("No index given."); 537 | } 538 | 539 | List tmp = new ArrayList(); 540 | for (Integer index : indexes) { 541 | tmp.add(index.toString()); 542 | } 543 | String items = String.format("index(es) '%s'", Python.join(", ", tmp)); 544 | logging.info(String.format("Unselecting %s from list '%s'.", items, locator)); 545 | 546 | Select select = getSelectList(locator); 547 | 548 | if (!isMultiselectList(select)) { 549 | throw new Selenium2LibraryNonFatalException( 550 | "Keyword 'Unselect from list' works only for multiselect lists."); 551 | } 552 | 553 | for (int index : indexes) { 554 | select.deselectByIndex(index); 555 | } 556 | } 557 | 558 | /** 559 | * Unselect the given *values of the multi-select list identified by 560 | * locator.
561 | *
562 | * Select list keywords work on both lists and combo boxes. Key attributes 563 | * for select lists are id and name. See `Introduction` for details about 564 | * locators.
565 | * 566 | * @param locator 567 | * The locator to locate the multi-select list. 568 | * @param values 569 | * The list of values to select 570 | */ 571 | @RobotKeyword 572 | @ArgumentNames({ "locator", "*values" }) 573 | public void unselectFromListByValue(String locator, String... values) { 574 | if (values.equals(null)) { 575 | throw new Selenium2LibraryNonFatalException("No value given."); 576 | } 577 | 578 | String items = String.format("value(s) '%s'", Python.join(", ", values)); 579 | logging.info(String.format("Unselecting %s from list '%s'.", items, locator)); 580 | 581 | Select select = getSelectList(locator); 582 | 583 | if (!isMultiselectList(select)) { 584 | throw new Selenium2LibraryNonFatalException( 585 | "Keyword 'Unselect from list' works only for multiselect lists."); 586 | } 587 | 588 | for (String value : values) { 589 | select.deselectByValue(value); 590 | } 591 | } 592 | 593 | /** 594 | * Unselect the given *labels of the multi-select list identified by 595 | * locator.
596 | *
597 | * Select list keywords work on both lists and combo boxes. Key attributes 598 | * for select lists are id and name. See `Introduction` for details about 599 | * locators.
600 | * 601 | * @param locator 602 | * The locator to locate the multi-select list. 603 | * @param labels 604 | * The list of labels to select 605 | */ 606 | @RobotKeyword 607 | @ArgumentNames({ "locator", "*labels" }) 608 | public void unselectFromListByLabel(String locator, String... labels) { 609 | if (labels.equals(null)) { 610 | throw new Selenium2LibraryNonFatalException("No value given."); 611 | } 612 | 613 | String items = String.format("label(s) '%s'", Python.join(", ", labels)); 614 | logging.info(String.format("Unselecting %s from list '%s'.", items, locator)); 615 | 616 | Select select = getSelectList(locator); 617 | 618 | if (!isMultiselectList(select)) { 619 | throw new Selenium2LibraryNonFatalException( 620 | "Keyword 'Unselect from list' works only for multiselect lists."); 621 | } 622 | 623 | for (String label : labels) { 624 | select.deselectByVisibleText(label); 625 | } 626 | } 627 | 628 | // ############################## 629 | // Internal Methods 630 | // ############################## 631 | 632 | protected List getLabelsForOptions(List options) { 633 | List labels = new ArrayList(); 634 | 635 | for (WebElement option : options) { 636 | labels.add(option.getText()); 637 | } 638 | 639 | return labels; 640 | } 641 | 642 | protected Select getSelectList(String locator) { 643 | List webElements = element.elementFind(locator, true, true, "select"); 644 | 645 | return new Select(webElements.get(0)); 646 | } 647 | 648 | protected List getSelectListOptions(Select select) { 649 | return new ArrayList(select.getOptions()); 650 | } 651 | 652 | protected List getSelectListOptions(String locator) { 653 | Select select = getSelectList(locator); 654 | 655 | return getSelectListOptions(select); 656 | } 657 | 658 | protected List getSelectListOptionsSelected(String locator) { 659 | Select select = getSelectList(locator); 660 | 661 | return new ArrayList(select.getAllSelectedOptions()); 662 | } 663 | 664 | protected List getValuesForOptions(List options) { 665 | ArrayList labels = new ArrayList(); 666 | 667 | for (WebElement option : options) { 668 | labels.add(option.getAttribute("value")); 669 | } 670 | 671 | return labels; 672 | } 673 | 674 | protected boolean isMultiselectList(Select select) { 675 | return select.isMultiple(); 676 | } 677 | 678 | } 679 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/keywords/TableElement.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.keywords; 2 | 3 | import java.util.List; 4 | 5 | import org.openqa.selenium.By; 6 | import org.openqa.selenium.WebElement; 7 | import org.robotframework.javalib.annotation.ArgumentNames; 8 | import org.robotframework.javalib.annotation.Autowired; 9 | import org.robotframework.javalib.annotation.RobotKeyword; 10 | import org.robotframework.javalib.annotation.RobotKeywordOverload; 11 | import org.robotframework.javalib.annotation.RobotKeywords; 12 | 13 | import com.github.markusbernhardt.selenium2library.RunOnFailureKeywordsAdapter; 14 | import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; 15 | import com.github.markusbernhardt.selenium2library.locators.TableElementFinder; 16 | 17 | @RobotKeywords 18 | public class TableElement extends RunOnFailureKeywordsAdapter { 19 | 20 | /** 21 | * Instantiated BrowserManagement keyword bean 22 | */ 23 | @Autowired 24 | protected BrowserManagement browserManagement; 25 | 26 | /** 27 | * Instantiated Logging keyword bean 28 | */ 29 | @Autowired 30 | protected Logging logging; 31 | 32 | // ############################## 33 | // Keywords 34 | // ############################## 35 | 36 | @RobotKeywordOverload 37 | public String getTableCell(String tableLocator, int row, int column) { 38 | return getTableCell(tableLocator, row, column, "INFO"); 39 | } 40 | 41 | /** 42 | * Returns the content of the table cell at the coordinates row and 43 | * column of the table identified by tableLocator.
44 | *
45 | * Row and column number start from 1. Header and footer rows are included 46 | * in the count. That way also cell content from header or footer rows can 47 | * be obtained with this keyword.
48 | *
49 | * Key attributes for tables are id and name. See `Introduction` for details 50 | * about locators and log levels.
51 | * 52 | * @param tableLocator 53 | * The locator to locate the table. 54 | * @param row 55 | * The table row. 56 | * @param column 57 | * The table column. 58 | * @param logLevel 59 | * Default=INFO. Optional log level. 60 | * @return The table cell content. 61 | */ 62 | @RobotKeyword 63 | @ArgumentNames({ "tableLocator", "row", "column", "logLevel=INFO" }) 64 | public String getTableCell(String tableLocator, int row, int column, String logLevel) { 65 | int rowIndex = row - 1; 66 | int columnIndex = column - 1; 67 | WebElement table = TableElementFinder.find(browserManagement.getCurrentWebDriver(), tableLocator); 68 | if (table != null) { 69 | List rows = table.findElements(By.xpath("./thead/tr")); 70 | if (rowIndex >= rows.size()) { 71 | rows.addAll(table.findElements(By.xpath("./tbody/tr"))); 72 | } 73 | if (rowIndex >= rows.size()) { 74 | rows.addAll(table.findElements(By.xpath("./tfoot/tr"))); 75 | } 76 | if (rowIndex < rows.size()) { 77 | List columns = rows.get(rowIndex).findElements(By.tagName("th")); 78 | if (columnIndex >= columns.size()) { 79 | columns.addAll(rows.get(rowIndex).findElements(By.tagName("td"))); 80 | } 81 | if (columnIndex < columns.size()) { 82 | return columns.get(columnIndex).getText(); 83 | } 84 | } 85 | } 86 | logging.logSource(logLevel); 87 | throw new Selenium2LibraryNonFatalException(String.format( 88 | "Cell in table %s in row #%d and column #%d could not be found.", tableLocator, row, column)); 89 | } 90 | 91 | @RobotKeywordOverload 92 | public void tableCellShouldContain(String tableLocator, int row, int column, String text) { 93 | tableCellShouldContain(tableLocator, row, column, text, "INFO"); 94 | } 95 | 96 | /** 97 | * Verify the content of the table cell at the coordinates row and 98 | * column of the table identified by tableLocator contains 99 | * text.
100 | *
101 | * Row and column number start from 1. Header and footer rows are included 102 | * in the count. That way also cell content from header or footer rows can 103 | * be obtained with this keyword.
104 | *
105 | * Key attributes for tables are id and name. See `Introduction` for details 106 | * about locators and log levels.
107 | * 108 | * @param tableLocator 109 | * The locator to locate the table. 110 | * @param row 111 | * The table row. 112 | * @param column 113 | * The table column. 114 | * @param text 115 | * The text to verify. 116 | * @param logLevel 117 | * Default=INFO. Optional log level. 118 | */ 119 | @RobotKeyword 120 | @ArgumentNames({ "tableLocator", "row", "column", "text", "logLevel=INFO" }) 121 | public void tableCellShouldContain(String tableLocator, int row, int column, String text, String logLevel) { 122 | String message = String.format("Cell in table '%s' in row #%d and column #%d should have contained text '%s'.", 123 | tableLocator, row, column, text); 124 | 125 | String content = ""; 126 | try { 127 | content = getTableCell(tableLocator, row, column, logLevel); 128 | } catch (Selenium2LibraryNonFatalException e) { 129 | logging.info(e.getMessage()); 130 | throw new Selenium2LibraryNonFatalException(message); 131 | } 132 | 133 | logging.info(String.format("Cell contains %s.", content)); 134 | if (!content.contains(text)) { 135 | logging.logSource(logLevel); 136 | throw new Selenium2LibraryNonFatalException(message); 137 | } 138 | } 139 | 140 | @RobotKeywordOverload 141 | public void tableColumnShouldContain(String tableLocator, int col, String text) { 142 | tableColumnShouldContain(tableLocator, col, text, "INFO"); 143 | } 144 | 145 | /** 146 | * Verify the content of any table cells of the table column of the 147 | * table identified by tableLocator contains text.
148 | *
149 | * Key attributes for tables are id and name. See `Introduction` for details 150 | * about locators and log levels.
151 | *
152 | * The first leftmost column is column number 1. If the table contains cells 153 | * that span multiple columns, those merged cells count as a single column. 154 | * For example both tests below work, if in one row columns A and B are 155 | * merged with colspan="2", and the logical third column contains "C".
156 | *
157 | * Example: 158 | * 159 | * 160 | * 161 | * 162 | * 163 | * 164 | * 165 | * 166 | * 167 | * 168 | * 169 | * 170 | * 171 | *
Table Column Should ContaintableId3C
Table Column Should ContaintableId2C
172 | * 173 | * @param tableLocator 174 | * The locator to locate the table. 175 | * @param col 176 | * The table column. 177 | * @param text 178 | * The text to verify. 179 | * @param logLevel 180 | * Default=INFO. Optional log level. 181 | */ 182 | @RobotKeyword 183 | @ArgumentNames({ "tableLocator", "col", "text", "logLevel=INFO" }) 184 | public void tableColumnShouldContain(String tableLocator, int col, String text, String logLevel) { 185 | WebElement element = TableElementFinder.findByCol(browserManagement.getCurrentWebDriver(), tableLocator, col, 186 | text); 187 | if (element == null) { 188 | logging.logSource(logLevel); 189 | throw new Selenium2LibraryNonFatalException(String.format( 190 | "Column #%d in table identified by '%s' should have contained text '%s'.", col, tableLocator, text)); 191 | } 192 | } 193 | 194 | @RobotKeywordOverload 195 | public void tableFooterShouldContain(String tableLocator, String text) { 196 | tableFooterShouldContain(tableLocator, text, "INFO"); 197 | } 198 | 199 | /** 200 | * Verify the content of any table footer cells of the table identified by 201 | * tableLocator contains text.
202 | *
203 | * Key attributes for tables are id and name. See `Introduction` for details 204 | * about locators and log levels.
205 | * 206 | * @param tableLocator 207 | * The locator to locate the table. 208 | * @param text 209 | * The text to verify. 210 | * @param logLevel 211 | * Default=INFO. Optional log level. 212 | */ 213 | @RobotKeyword 214 | @ArgumentNames({ "tableLocator", "text", "logLevel=INFO" }) 215 | public void tableFooterShouldContain(String tableLocator, String text, String logLevel) { 216 | WebElement element = TableElementFinder.findByFooter(browserManagement.getCurrentWebDriver(), tableLocator, 217 | text); 218 | if (element == null) { 219 | logging.logSource(logLevel); 220 | throw new Selenium2LibraryNonFatalException(String.format( 221 | "Footer in table identified by '%s' should have contained text '%s'.", tableLocator, text)); 222 | } 223 | } 224 | 225 | @RobotKeywordOverload 226 | public void tableHeaderShouldContain(String tableLocator, String text) { 227 | tableHeaderShouldContain(tableLocator, text, "INFO"); 228 | } 229 | 230 | /** 231 | * Verify the content of any table header cells of the table identified by 232 | * tableLocator contains text.
233 | *
234 | * Key attributes for tables are id and name. See `Introduction` for details 235 | * about locators and log levels.
236 | * 237 | * @param tableLocator 238 | * The locator to locate the table. 239 | * @param text 240 | * The text to verify. 241 | * @param logLevel 242 | * Default=INFO. Optional log level. 243 | */ 244 | @RobotKeyword 245 | @ArgumentNames({ "tableLocator", "text", "logLevel=INFO" }) 246 | public void tableHeaderShouldContain(String tableLocator, String text, String logLevel) { 247 | WebElement element = TableElementFinder.findByHeader(browserManagement.getCurrentWebDriver(), tableLocator, 248 | text); 249 | if (element == null) { 250 | logging.logSource(logLevel); 251 | throw new Selenium2LibraryNonFatalException(String.format( 252 | "Header in table identified by '%s' should have contained text '%s'.", tableLocator, text)); 253 | } 254 | } 255 | 256 | @RobotKeywordOverload 257 | public void tableRowShouldContain(String tableLocator, int row, String text) { 258 | tableRowShouldContain(tableLocator, row, text, "INFO"); 259 | } 260 | 261 | /** 262 | * Verify the content of any table cells of the table row of the 263 | * table identified by tableLocator contains text.
264 | *
265 | * Key attributes for tables are id and name. See `Introduction` for details 266 | * about locators and log levels.
267 | *
268 | * The uppermost row is row number 1. For tables that are structured with 269 | * thead, tbody and tfoot, only the tbody section is searched. Please use 270 | * Table Header Should Contain or Table Footer Should Contain for tests 271 | * against the header or footer content.
272 | *
273 | * If the table contains cells that span multiple rows, a match only occurs 274 | * for the uppermost row of those merged cells.
275 | * 276 | * @param tableLocator 277 | * The locator to locate the table. 278 | * @param row 279 | * The table row. 280 | * @param text 281 | * The text to verify. 282 | * @param logLevel 283 | * Default=INFO. Optional log level. 284 | */ 285 | @RobotKeyword 286 | @ArgumentNames({ "tableLocator", "row", "text", "logLevel=INFO" }) 287 | public void tableRowShouldContain(String tableLocator, int row, String text, String logLevel) { 288 | WebElement element = TableElementFinder.findByRow(browserManagement.getCurrentWebDriver(), tableLocator, row, 289 | text); 290 | if (element == null) { 291 | logging.logSource(logLevel); 292 | throw new Selenium2LibraryNonFatalException(String.format( 293 | "Row #%d in table identified by '%s' should have contained text '%s'.", row, tableLocator, text)); 294 | } 295 | } 296 | 297 | @RobotKeywordOverload 298 | public void tableShouldContain(String tableLocator, String text) { 299 | tableShouldContain(tableLocator, text, "INFO"); 300 | } 301 | 302 | /** 303 | * Verify the content of any table cells of the table identified by 304 | * tableLocator contains text.
305 | *
306 | * Key attributes for tables are id and name. See `Introduction` for details 307 | * about locators and log levels.
308 | * 309 | * @param tableLocator 310 | * The locator to locate the table. 311 | * @param text 312 | * The text to verify. 313 | * @param logLevel 314 | * Default=INFO. Optional log level. 315 | */ 316 | @RobotKeyword 317 | @ArgumentNames({ "tableLocator", "text", "logLevel=INFO" }) 318 | public void tableShouldContain(String tableLocator, String text, String logLevel) { 319 | WebElement element = TableElementFinder.findByContent(browserManagement.getCurrentWebDriver(), tableLocator, 320 | text); 321 | if (element == null) { 322 | logging.logSource(logLevel); 323 | throw new Selenium2LibraryNonFatalException(String.format( 324 | "Table identified by '%s' should have contained text '%s'.", tableLocator, text)); 325 | } 326 | } 327 | 328 | } 329 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/locators/ElementFinder.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.locators; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Hashtable; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Map.Entry; 8 | import java.util.TreeMap; 9 | 10 | import org.openqa.selenium.By; 11 | import org.openqa.selenium.JavascriptExecutor; 12 | import org.openqa.selenium.SearchContext; 13 | import org.openqa.selenium.WebDriver; 14 | import org.openqa.selenium.WebElement; 15 | import org.python.util.PythonInterpreter; 16 | 17 | import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; 18 | import com.github.markusbernhardt.selenium2library.keywords.Element; 19 | import com.github.markusbernhardt.selenium2library.utils.Python; 20 | 21 | public class ElementFinder { 22 | 23 | protected final static Hashtable registeredLocationStrategies = new Hashtable(); 24 | 25 | protected enum KeyAttrs { 26 | DEFAULT("@id,@name"), A("@id,@name,@href,normalize-space(descendant-or-self::text())"), IMG( 27 | "@id,@name,@src,@alt"), INPUT("@id,@name,@value,@src"), BUTTON( 28 | "@id,@name,@value,normalize-space(descendant-or-self::text())"); 29 | 30 | protected String[] keyAttrs; 31 | 32 | KeyAttrs(String keyAttrs) { 33 | this.keyAttrs = keyAttrs.split(","); 34 | } 35 | 36 | public String[] getKeyAttrs() { 37 | return keyAttrs; 38 | } 39 | } 40 | 41 | protected interface Strategy { 42 | List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates); 43 | 44 | }; 45 | 46 | protected enum StrategyEnum implements Strategy { 47 | DEFAULT { 48 | 49 | @Override 50 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 51 | if (findByCoordinates.criteria.startsWith("//")) { 52 | return XPATH.findBy(webDriver, findByCoordinates); 53 | } 54 | return findByKeyAttrs(webDriver, findByCoordinates); 55 | } 56 | }, 57 | IDENTIFIER { 58 | 59 | @Override 60 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 61 | List elements = webDriver.findElements(By.id(findByCoordinates.criteria)); 62 | elements.addAll(webDriver.findElements(By.name(findByCoordinates.criteria))); 63 | return filterElements(elements, findByCoordinates); 64 | } 65 | }, 66 | ID { 67 | 68 | @Override 69 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 70 | return filterElements(webDriver.findElements(By.id(findByCoordinates.criteria)), findByCoordinates); 71 | } 72 | }, 73 | NAME { 74 | 75 | @Override 76 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 77 | return filterElements(webDriver.findElements(By.name(findByCoordinates.criteria)), findByCoordinates); 78 | } 79 | }, 80 | XPATH { 81 | 82 | @Override 83 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 84 | return filterElements(webDriver.findElements(By.xpath(findByCoordinates.criteria)), findByCoordinates); 85 | } 86 | }, 87 | DOM { 88 | 89 | @Override 90 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 91 | Object result = ((JavascriptExecutor) webDriver).executeScript(String.format("return %s;", 92 | findByCoordinates.criteria)); 93 | return filterElements(toList(result), findByCoordinates); 94 | } 95 | }, 96 | LINK { 97 | 98 | @Override 99 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 100 | return filterElements(webDriver.findElements(By.linkText(findByCoordinates.criteria)), 101 | findByCoordinates); 102 | } 103 | }, 104 | CSS { 105 | 106 | @Override 107 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 108 | return filterElements(webDriver.findElements(By.cssSelector(findByCoordinates.criteria)), 109 | findByCoordinates); 110 | } 111 | }, 112 | TAG { 113 | 114 | @Override 115 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 116 | return filterElements(webDriver.findElements(By.tagName(findByCoordinates.criteria)), findByCoordinates); 117 | } 118 | }, 119 | JQUERY { 120 | 121 | @Override 122 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 123 | 124 | return findByJQuerySizzle(webDriver, findByCoordinates); 125 | } 126 | 127 | }, 128 | SIZZLE { 129 | 130 | @Override 131 | public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) { 132 | 133 | return findByJQuerySizzle(webDriver, findByCoordinates); 134 | } 135 | 136 | }; 137 | 138 | } 139 | 140 | protected static List findByJQuerySizzle(WebDriver webDriver, FindByCoordinates findByCoordinates) { 141 | String js = String.format("return jQuery('%s').get();", findByCoordinates.criteria.replace("'", "\\'")); 142 | 143 | Object o = ((JavascriptExecutor) webDriver).executeScript(js); 144 | List list = toList(o); 145 | return filterElements(list, findByCoordinates); 146 | } 147 | 148 | protected static List filterElements(List elements, FindByCoordinates findByCoordinates) { 149 | if (findByCoordinates.tag == null) { 150 | return elements; 151 | } 152 | 153 | List result = new ArrayList(); 154 | for (WebElement element : elements) { 155 | if (elementMatches(element, findByCoordinates)) { 156 | result.add(element); 157 | } 158 | } 159 | return result; 160 | } 161 | 162 | protected static boolean elementMatches(WebElement element, FindByCoordinates findByCoordinates) { 163 | if (!element.getTagName().toLowerCase().equals(findByCoordinates.tag)) { 164 | return false; 165 | } 166 | 167 | if (findByCoordinates.constraints != null) { 168 | for (String name : findByCoordinates.constraints.keySet()) { 169 | if (!element.getAttribute(name).equals(findByCoordinates.constraints.get(name))) { 170 | return false; 171 | } 172 | } 173 | } 174 | 175 | return true; 176 | } 177 | 178 | protected static List findByKeyAttrs(WebDriver webDriver, FindByCoordinates findByCoordinates) { 179 | KeyAttrs keyAttrs = KeyAttrs.DEFAULT; 180 | if (findByCoordinates.tag != null) { 181 | try { 182 | keyAttrs = KeyAttrs.valueOf(findByCoordinates.tag.trim().toUpperCase()); 183 | } catch (IllegalArgumentException e) { 184 | // No special keyAttrs available for this tag 185 | } 186 | } 187 | String xpathCriteria = Element.escapeXpathValue(findByCoordinates.criteria); 188 | String xpathTag = findByCoordinates.tag; 189 | if (findByCoordinates.tag == null) { 190 | xpathTag = "*"; 191 | } 192 | List xpathConstraints = new ArrayList(); 193 | if (findByCoordinates.constraints != null) { 194 | for (Entry entry : findByCoordinates.constraints.entrySet()) { 195 | xpathConstraints.add(String.format("@%s='%s'", entry.getKey(), entry.getValue())); 196 | } 197 | } 198 | List xpathSearchers = new ArrayList(); 199 | for (String attr : keyAttrs.getKeyAttrs()) { 200 | xpathSearchers.add(String.format("%s=%s", attr, xpathCriteria)); 201 | } 202 | xpathSearchers.addAll(getAttrsWithUrl(webDriver, keyAttrs, findByCoordinates.criteria)); 203 | String xpath = String.format("//%s[%s(%s)]", xpathTag, Python.join(" and ", xpathConstraints) 204 | + (xpathConstraints.size() > 0 ? " and " : ""), Python.join(" or ", xpathSearchers)); 205 | 206 | return webDriver.findElements(By.xpath(xpath)); 207 | } 208 | 209 | protected static List getAttrsWithUrl(WebDriver webDriver, KeyAttrs keyAttrs, String criteria) { 210 | List attrs = new ArrayList(); 211 | String url = null; 212 | String xpathUrl = null; 213 | String[] srcHref = { "@src", "@href" }; 214 | for (String attr : srcHref) { 215 | for (String keyAttr : keyAttrs.getKeyAttrs()) { 216 | if (attr.equals(keyAttr)) { 217 | if (url == null || xpathUrl == null) { 218 | url = getBaseUrl(webDriver) + "/" + criteria; 219 | xpathUrl = Element.escapeXpathValue(url); 220 | } 221 | attrs.add(String.format("%s=%s", attr, xpathUrl)); 222 | } 223 | } 224 | } 225 | return attrs; 226 | } 227 | 228 | protected static String getBaseUrl(WebDriver webDriver) { 229 | String url = webDriver.getCurrentUrl(); 230 | int lastIndex = url.lastIndexOf('/'); 231 | if (lastIndex != -1) { 232 | url = url.substring(0, lastIndex); 233 | } 234 | return url; 235 | } 236 | 237 | public static void addLocationStrategy(String strategyName, String functionDefinition, String delimiter) { 238 | registeredLocationStrategies.put(strategyName.toUpperCase(), new CustomStrategy(functionDefinition, delimiter)); 239 | } 240 | 241 | public static List find(WebDriver webDriver, String locator) { 242 | return find(webDriver, locator, null); 243 | } 244 | 245 | public static List find(WebDriver webDriver, String locator, String tag) { 246 | if (webDriver == null) { 247 | throw new Selenium2LibraryNonFatalException("ElementFinder.find: webDriver is null."); 248 | } 249 | if (locator == null) { 250 | throw new Selenium2LibraryNonFatalException("ElementFinder.find: locator is null."); 251 | } 252 | 253 | FindByCoordinates findByCoordinates = new FindByCoordinates(); 254 | Strategy strategy = parseLocator(findByCoordinates, locator); 255 | parseTag(findByCoordinates, strategy, tag); 256 | return strategy.findBy(webDriver, findByCoordinates); 257 | } 258 | 259 | protected static ThreadLocal loggingPythonInterpreter = new ThreadLocal() { 260 | 261 | @Override 262 | protected PythonInterpreter initialValue() { 263 | PythonInterpreter pythonInterpreter = new PythonInterpreter(); 264 | pythonInterpreter.exec("from robot.libraries.BuiltIn import BuiltIn; from robot.api import logger;"); 265 | return pythonInterpreter; 266 | } 267 | }; 268 | 269 | protected static void warn(String msg) { 270 | loggingPythonInterpreter.get().exec( 271 | String.format("logger.warn('%s');", msg.replace("'", "\\'").replace("\n", "\\n"))); 272 | } 273 | 274 | protected static Strategy parseLocator(FindByCoordinates findByCoordinates, String locator) { 275 | String prefix = null; 276 | String criteria = locator; 277 | if (!locator.startsWith("//")) { 278 | String[] locatorParts = locator.split("=", 2); 279 | if (locatorParts.length == 2) { 280 | prefix = locatorParts[0].trim().toUpperCase(); 281 | criteria = locatorParts[1].trim(); 282 | } 283 | } 284 | 285 | Strategy strategy = StrategyEnum.DEFAULT; 286 | if (prefix != null) { 287 | try { 288 | strategy = StrategyEnum.valueOf(prefix); 289 | } catch (IllegalArgumentException e) { 290 | // No standard locator type. Look for custom strategy 291 | CustomStrategy customStrategy = registeredLocationStrategies.get(prefix); 292 | if (customStrategy != null) { 293 | strategy = customStrategy; 294 | } 295 | } 296 | } 297 | findByCoordinates.criteria = criteria; 298 | return strategy; 299 | } 300 | 301 | protected static void parseTag(FindByCoordinates findByCoordinates, Strategy strategy, String tag) { 302 | if (tag == null) { 303 | return; 304 | } 305 | tag = tag.toLowerCase(); 306 | Map constraints = new TreeMap(); 307 | if (tag.equals("link")) { 308 | tag = "a"; 309 | } else if (tag.equals("image")) { 310 | tag = "img"; 311 | } else if (tag.equals("list")) { 312 | tag = "select"; 313 | } else if (tag.equals("text area")) { 314 | tag = "textarea"; 315 | } else if (tag.equals("radio button")) { 316 | tag = "input"; 317 | constraints.put("type", "radio"); 318 | } else if (tag.equals("checkbox")) { 319 | tag = "input"; 320 | constraints.put("type", "checkbox"); 321 | } else if (tag.equals("text field")) { 322 | tag = "input"; 323 | constraints.put("type", "text"); 324 | } else if (tag.equals("file upload")) { 325 | tag = "input"; 326 | constraints.put("type", "file"); 327 | } 328 | findByCoordinates.tag = tag; 329 | findByCoordinates.constraints = constraints; 330 | } 331 | 332 | @SuppressWarnings("unchecked") 333 | protected static List toList(Object o) { 334 | if (o instanceof List) { 335 | return (List) o; 336 | } 337 | List list = new ArrayList(); 338 | if (o instanceof WebElement) { 339 | list.add((WebElement) o); 340 | return list; 341 | } 342 | return list; 343 | } 344 | 345 | protected static class FindByCoordinates { 346 | 347 | String criteria; 348 | String tag; 349 | Map constraints; 350 | } 351 | 352 | protected static class CustomStrategy implements Strategy { 353 | 354 | protected String functionDefinition; 355 | 356 | protected String delimiter; 357 | 358 | public CustomStrategy(String functionDefinition, String delimiter) { 359 | this.functionDefinition = functionDefinition; 360 | this.delimiter = delimiter; 361 | } 362 | 363 | @Override 364 | public List findBy(final WebDriver webDriver, final FindByCoordinates findByCoordinates) { 365 | return filterElements(webDriver.findElements(new By() { 366 | 367 | @Override 368 | public List findElements(SearchContext context) { 369 | Object[] arguments = null; 370 | if (delimiter == null) { 371 | arguments = new Object[1]; 372 | arguments[0] = findByCoordinates.criteria; 373 | } else { 374 | String[] splittedCriteria = findByCoordinates.criteria.split(delimiter); 375 | arguments = new Object[splittedCriteria.length]; 376 | for (int i = 0; i < splittedCriteria.length; i++) { 377 | arguments[i] = splittedCriteria[i]; 378 | } 379 | } 380 | Object o = ((JavascriptExecutor) webDriver).executeScript(functionDefinition, arguments); 381 | return toList(o); 382 | } 383 | 384 | }), findByCoordinates); 385 | } 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/locators/TableElementFinder.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.locators; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.TreeMap; 7 | 8 | import org.openqa.selenium.WebDriver; 9 | import org.openqa.selenium.WebElement; 10 | 11 | public class TableElementFinder { 12 | 13 | protected final static TreeMap> locatorSuffixesMap; 14 | 15 | static { 16 | locatorSuffixesMap = new TreeMap>(); 17 | addLocatorSuffix(locatorSuffixesMap, "css.default", ""); 18 | addLocatorSuffix(locatorSuffixesMap, "css.content", ""); 19 | addLocatorSuffix(locatorSuffixesMap, "css.header", " th"); 20 | addLocatorSuffix(locatorSuffixesMap, "css.footer", " tfoot td"); 21 | addLocatorSuffix(locatorSuffixesMap, "css.row", " tr:nth-child(%s)"); 22 | addLocatorSuffix(locatorSuffixesMap, "css.col", " tr td:nth-child(%s)", " tr th:nth-child(%s)"); 23 | 24 | addLocatorSuffix(locatorSuffixesMap, "sizzle.default", ""); 25 | addLocatorSuffix(locatorSuffixesMap, "sizzle.content", ""); 26 | addLocatorSuffix(locatorSuffixesMap, "sizzle.header", " th"); 27 | addLocatorSuffix(locatorSuffixesMap, "sizzle.footer", " tfoot td"); 28 | addLocatorSuffix(locatorSuffixesMap, "sizzle.row", " tr:nth-child(%s)"); 29 | addLocatorSuffix(locatorSuffixesMap, "sizzle.col", " tr td:nth-child(%s)", " tr th:nth-child(%s)"); 30 | 31 | addLocatorSuffix(locatorSuffixesMap, "xpath.default", ""); 32 | addLocatorSuffix(locatorSuffixesMap, "xpath.content", "//*"); 33 | addLocatorSuffix(locatorSuffixesMap, "xpath.header", "//th"); 34 | addLocatorSuffix(locatorSuffixesMap, "xpath.footer", "//tfoot//td"); 35 | addLocatorSuffix(locatorSuffixesMap, "xpath.row", "//tr[%s]//*"); 36 | addLocatorSuffix(locatorSuffixesMap, "xpath.col", "//tr//*[self::td or self::th][%s]"); 37 | } 38 | 39 | public static WebElement find(WebDriver webDriver, String tableLocator) { 40 | List locators = parseTableLocator(tableLocator, "default"); 41 | return searchInLocators(webDriver, locators, null); 42 | } 43 | 44 | public static WebElement findByContent(WebDriver webDriver, String tableLocator, String content) { 45 | List locators = parseTableLocator(tableLocator, "content"); 46 | return searchInLocators(webDriver, locators, content); 47 | } 48 | 49 | public static WebElement findByHeader(WebDriver webDriver, String tableLocator, String content) { 50 | List locators = parseTableLocator(tableLocator, "header"); 51 | return searchInLocators(webDriver, locators, content); 52 | } 53 | 54 | public static WebElement findByFooter(WebDriver webDriver, String tableLocator, String content) { 55 | List locators = parseTableLocator(tableLocator, "footer"); 56 | return searchInLocators(webDriver, locators, content); 57 | } 58 | 59 | public static WebElement findByRow(WebDriver webDriver, String tableLocator, int row, String content) { 60 | List locators = parseTableLocator(tableLocator, "row"); 61 | List formattedLocators = new ArrayList(); 62 | for (String locator : locators) { 63 | formattedLocators.add(String.format(locator, Integer.toString(row))); 64 | } 65 | return searchInLocators(webDriver, formattedLocators, content); 66 | } 67 | 68 | public static WebElement findByCol(WebDriver webDriver, String tableLocator, int col, String content) { 69 | List locators = parseTableLocator(tableLocator, "col"); 70 | List formattedLocators = new ArrayList(); 71 | for (String locator : locators) { 72 | formattedLocators.add(String.format(locator, Integer.toString(col))); 73 | } 74 | return searchInLocators(webDriver, formattedLocators, content); 75 | } 76 | 77 | protected static void addLocatorSuffix(Map> locatorSuffixesMap, String key, String... values) { 78 | List list = new ArrayList(); 79 | for (String value : values) { 80 | list.add(value); 81 | } 82 | locatorSuffixesMap.put(key, list); 83 | } 84 | 85 | protected static List parseTableLocator(String tableLocator, String locationMethod) { 86 | String tableLocatorType = null; 87 | 88 | if (tableLocator.startsWith("xpath=")) { 89 | tableLocatorType = "xpath."; 90 | } else if (tableLocator.startsWith("jquery=") || tableLocator.startsWith("sizzle=")) { 91 | tableLocatorType = "sizzle."; 92 | } else { 93 | if (!tableLocator.startsWith("css=")) { 94 | tableLocator = String.format("css=table#%s", tableLocator); 95 | } 96 | tableLocatorType = "css."; 97 | } 98 | 99 | List locatorSuffixes = locatorSuffixesMap.get(tableLocatorType + locationMethod); 100 | 101 | List parsedTabeLocators = new ArrayList(); 102 | for (String locatorSuffix : locatorSuffixes) { 103 | parsedTabeLocators.add(tableLocator + locatorSuffix); 104 | } 105 | return parsedTabeLocators; 106 | } 107 | 108 | protected static WebElement searchInLocators(WebDriver webDriver, List locators, String content) { 109 | for (String locator : locators) { 110 | List elements = ElementFinder.find(webDriver, locator); 111 | for (WebElement element : elements) { 112 | if (content == null) { 113 | return element; 114 | } 115 | String elementText = element.getText(); 116 | if (elementText != null && elementText.contains(content)) { 117 | return element; 118 | } 119 | } 120 | } 121 | return null; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/locators/WindowManager.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.locators; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.openqa.selenium.JavascriptExecutor; 7 | import org.openqa.selenium.NoSuchWindowException; 8 | import org.openqa.selenium.WebDriver; 9 | 10 | import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; 11 | 12 | public class WindowManager { 13 | 14 | public static int WINDOW_INFO_INDEX_WINDOW_ID = 0; 15 | public static int WINDOW_INFO_INDEX_WINDOW_NAME = 1; 16 | public static int WINDOW_INFO_INDEX_DOCUMENT_TITLE = 2; 17 | public static int WINDOW_INFO_INDEX_DOCUMENT_URL = 3; 18 | 19 | protected enum WindowManagerStrategy { 20 | DEFAULT { 21 | 22 | @Override 23 | public void select(WebDriver webDriver, SelectCoordinates selectCoordinates) { 24 | if (selectCoordinates.criteria == null || selectCoordinates.criteria.toLowerCase().equals("null")) { 25 | webDriver.switchTo().window(""); 26 | return; 27 | } 28 | 29 | try { 30 | NAME.select(webDriver, selectCoordinates); 31 | return; 32 | } catch (Throwable t) { 33 | } 34 | 35 | try { 36 | TITLE.select(webDriver, selectCoordinates); 37 | return; 38 | } catch (Throwable t) { 39 | } 40 | throw new Selenium2LibraryNonFatalException("Unable to locate window with name or title '" 41 | + selectCoordinates.criteria + "'"); 42 | } 43 | }, 44 | TITLE { 45 | 46 | @Override 47 | public void select(WebDriver webDriver, final SelectCoordinates selectCoordinates) { 48 | selectMatching(webDriver, new Matcher() { 49 | 50 | @Override 51 | public boolean match(List currentWindowInfo) { 52 | return currentWindowInfo.get(WINDOW_INFO_INDEX_DOCUMENT_TITLE).trim().toLowerCase() 53 | .equals(selectCoordinates.criteria.toLowerCase()); 54 | } 55 | 56 | }, "Unable to locate window with title '" + selectCoordinates.criteria + "'"); 57 | } 58 | }, 59 | NAME { 60 | 61 | @Override 62 | public void select(WebDriver webDriver, final SelectCoordinates selectCoordinates) { 63 | selectMatching(webDriver, new Matcher() { 64 | 65 | @Override 66 | public boolean match(List currentWindowInfo) { 67 | return currentWindowInfo.get(WINDOW_INFO_INDEX_WINDOW_NAME).trim().toLowerCase() 68 | .equals(selectCoordinates.criteria.toLowerCase()); 69 | } 70 | 71 | }, "Unable to locate window with name '" + selectCoordinates.criteria + "'"); 72 | } 73 | }, 74 | URL { 75 | 76 | @Override 77 | public void select(WebDriver webDriver, final SelectCoordinates selectCoordinates) { 78 | selectMatching(webDriver, new Matcher() { 79 | 80 | @Override 81 | public boolean match(List currentWindowInfo) { 82 | return currentWindowInfo.get(WINDOW_INFO_INDEX_DOCUMENT_URL).trim().toLowerCase() 83 | .equals(selectCoordinates.criteria.toLowerCase()); 84 | } 85 | 86 | }, "Unable to locate window with URL '" + selectCoordinates.criteria + "'"); 87 | } 88 | }; 89 | 90 | abstract public void select(WebDriver webDriver, SelectCoordinates selectCoordinates); 91 | 92 | protected static void selectMatching(WebDriver webDriver, Matcher matcher, String error) { 93 | String startingHandle = null; 94 | try { 95 | startingHandle = webDriver.getWindowHandle(); 96 | } catch (NoSuchWindowException e) { 97 | // Window of current WebDriver instance is already closed 98 | } 99 | 100 | for (String handle : webDriver.getWindowHandles()) { 101 | webDriver.switchTo().window(handle); 102 | if (matcher.match(getCurrentWindowInfo(webDriver))) { 103 | return; 104 | } 105 | } 106 | 107 | if (startingHandle != null) { 108 | webDriver.switchTo().window(startingHandle); 109 | } 110 | throw new Selenium2LibraryNonFatalException(error); 111 | } 112 | } 113 | 114 | public static List getWindowIds(WebDriver webDriver) { 115 | List windowIds = new ArrayList(); 116 | for (List windowInfo : getWindowInfos(webDriver)) { 117 | windowIds.add(windowInfo.get(0)); 118 | } 119 | return windowIds; 120 | } 121 | 122 | public static List getWindowNames(WebDriver webDriver) { 123 | List windowNames = new ArrayList(); 124 | for (List windowInfo : getWindowInfos(webDriver)) { 125 | windowNames.add(windowInfo.get(1)); 126 | } 127 | return windowNames; 128 | } 129 | 130 | public static List getWindowTitles(WebDriver webDriver) { 131 | List windowTitles = new ArrayList(); 132 | for (List windowInfo : getWindowInfos(webDriver)) { 133 | windowTitles.add(windowInfo.get(2)); 134 | } 135 | return windowTitles; 136 | } 137 | 138 | public static List> getWindowInfos(WebDriver webDriver) { 139 | String startingHandle = null; 140 | try { 141 | startingHandle = webDriver.getWindowHandle(); 142 | } catch (NoSuchWindowException e) { 143 | // Window of current WebDriver instance is already closed 144 | } 145 | 146 | List> windowInfos = new ArrayList>(); 147 | try { 148 | for (String handle : webDriver.getWindowHandles()) { 149 | webDriver.switchTo().window(handle); 150 | windowInfos.add(getCurrentWindowInfo(webDriver)); 151 | } 152 | } finally { 153 | if (startingHandle != null) { 154 | webDriver.switchTo().window(startingHandle); 155 | } 156 | } 157 | return windowInfos; 158 | } 159 | 160 | public static void select(WebDriver webDriver, String locator) { 161 | if (webDriver == null) { 162 | throw new Selenium2LibraryNonFatalException("WindowManager.select: webDriver is null."); 163 | } 164 | 165 | SelectCoordinates selectCoordinates = new SelectCoordinates(); 166 | WindowManagerStrategy strategy = parseLocator(selectCoordinates, locator); 167 | strategy.select(webDriver, selectCoordinates); 168 | } 169 | 170 | protected static WindowManagerStrategy parseLocator(SelectCoordinates selectCoordinates, String locator) { 171 | String prefix = null; 172 | String criteria = locator; 173 | if (locator != null && locator.length() > 0) { 174 | String[] locatorParts = locator.split("=", 2); 175 | if (locatorParts.length == 2) { 176 | prefix = locatorParts[0].trim().toUpperCase(); 177 | criteria = locatorParts[1].trim(); 178 | } 179 | } 180 | 181 | if (prefix == null || prefix.equals("name")) { 182 | if (criteria == null || criteria.toLowerCase().equals("main")) { 183 | criteria = ""; 184 | } 185 | } 186 | 187 | WindowManagerStrategy strategy = WindowManagerStrategy.DEFAULT; 188 | if (prefix != null) { 189 | strategy = WindowManagerStrategy.valueOf(prefix); 190 | } 191 | selectCoordinates.criteria = criteria; 192 | return strategy; 193 | } 194 | 195 | @SuppressWarnings("unchecked") 196 | protected static List getCurrentWindowInfo(WebDriver webDriver) { 197 | return (List) ((JavascriptExecutor) webDriver) 198 | .executeScript("return [ window.id, window.name, document.title, document.url ];"); 199 | } 200 | 201 | protected static class SelectCoordinates { 202 | 203 | String criteria; 204 | } 205 | 206 | protected static interface Matcher { 207 | 208 | boolean match(List currentWindowInfo); 209 | 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/utils/Javadoc2Libdoc.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.utils; 2 | 3 | import java.io.InputStream; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import javax.xml.bind.JAXBContext; 8 | import javax.xml.bind.JAXBException; 9 | import javax.xml.bind.Unmarshaller; 10 | 11 | import com.github.markusbernhardt.xmldoclet.xjc.AnnotationInstance; 12 | import com.github.markusbernhardt.xmldoclet.xjc.Class; 13 | import com.github.markusbernhardt.xmldoclet.xjc.Constructor; 14 | import com.github.markusbernhardt.xmldoclet.xjc.Method; 15 | import com.github.markusbernhardt.xmldoclet.xjc.ObjectFactory; 16 | import com.github.markusbernhardt.xmldoclet.xjc.Package; 17 | import com.github.markusbernhardt.xmldoclet.xjc.Root; 18 | import com.github.markusbernhardt.xmldoclet.xjc.TagInfo; 19 | 20 | public class Javadoc2Libdoc { 21 | protected final Map keywordDocumentationMap; 22 | 23 | public Javadoc2Libdoc(java.lang.Class clazz) { 24 | InputStream inputStream = Thread.currentThread().getContextClassLoader() 25 | .getResourceAsStream(clazz.getName().replace('.', '/') + ".javadoc"); 26 | Root root = loadJavadocRoot(inputStream); 27 | keywordDocumentationMap = loadKeywordDocumentationMap(root, clazz.getName()); 28 | } 29 | 30 | public String getKeywordDocumentation(String keywordName) { 31 | return keywordDocumentationMap.get(keywordName); 32 | } 33 | 34 | protected Root loadJavadocRoot(InputStream inputStream) { 35 | try { 36 | JAXBContext context = JAXBContext.newInstance(Root.class); 37 | Unmarshaller unmarshaller = context.createUnmarshaller(); 38 | return (Root) unmarshaller.unmarshal(inputStream); 39 | } catch (JAXBException e) { 40 | return new ObjectFactory().createRoot(); 41 | } 42 | } 43 | 44 | protected Map loadKeywordDocumentationMap(Root root, String className) { 45 | Map keywordDocumentation = new HashMap(); 46 | for (Package packageNode : root.getPackage()) { 47 | for (Class classNode : packageNode.getClazz()) { 48 | if (className.equals(classNode.getQualified())) { 49 | keywordDocumentation.put("__intro__", formatComment(classNode)); 50 | Constructor constructorNodeWithComment = null; 51 | for (Constructor constructorNode : classNode.getConstructor()) { 52 | if (constructorNode.getComment() != null && constructorNode.getComment().trim().length() > 0) { 53 | constructorNodeWithComment = constructorNode; 54 | } 55 | } 56 | keywordDocumentation.put("__init__", formatComment(constructorNodeWithComment)); 57 | } 58 | for (Method methodNode : classNode.getMethod()) { 59 | for (AnnotationInstance annotationInstanceNode : methodNode.getAnnotation()) { 60 | if (annotationInstanceNode.getName().equals("RobotKeyword")) { 61 | keywordDocumentation.put(methodNode.getName(), formatComment(methodNode)); 62 | break; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | return keywordDocumentation; 69 | } 70 | 71 | protected String formatComment(Class classNode) { 72 | if (classNode.getComment() != null) { 73 | return classNode.getComment(); 74 | } 75 | return ""; 76 | } 77 | 78 | protected String formatComment(Constructor constructorNode) { 79 | if (constructorNode.getComment() != null) { 80 | return constructorNode.getComment(); 81 | } 82 | return ""; 83 | } 84 | 85 | protected String formatComment(Method methodNode) { 86 | StringBuilder stringBuilder = new StringBuilder(); 87 | 88 | if (methodNode.getComment() != null) { 89 | stringBuilder.append(methodNode.getComment()); 90 | } 91 | 92 | stringBuilder.append(formatParam(methodNode)); 93 | stringBuilder.append(formatReturn(methodNode)); 94 | stringBuilder.append(formatSee(methodNode)); 95 | 96 | return stringBuilder.toString(); 97 | } 98 | 99 | protected String formatParam(Method methodNode) { 100 | boolean hasTag = false; 101 | StringBuilder stringBuilderParam = new StringBuilder(); 102 | stringBuilderParam.append("
Parameters:
"); 103 | for (TagInfo tagInfo : methodNode.getTag()) { 104 | if (!tagInfo.getName().equals("@param")) { 105 | continue; 106 | } 107 | hasTag = true; 108 | String text = tagInfo.getText(); 109 | int index = text.indexOf('\n'); 110 | stringBuilderParam.append("    "); 111 | stringBuilderParam.append(text.substring(0, index)); 112 | stringBuilderParam.append(" "); 113 | stringBuilderParam.append(text.substring(index + 1).trim()); 114 | stringBuilderParam.append("
"); 115 | } 116 | if (hasTag) { 117 | return stringBuilderParam.toString(); 118 | } 119 | return ""; 120 | } 121 | 122 | protected String formatReturn(Method methodNode) { 123 | boolean hasTag = false; 124 | StringBuilder stringBuilderParam = new StringBuilder(); 125 | stringBuilderParam.append("
Returns:
"); 126 | for (TagInfo tagInfo : methodNode.getTag()) { 127 | if (!tagInfo.getName().equals("@return")) { 128 | continue; 129 | } 130 | hasTag = true; 131 | String text = tagInfo.getText(); 132 | stringBuilderParam.append("    "); 133 | stringBuilderParam.append(text); 134 | stringBuilderParam.append("
"); 135 | } 136 | if (hasTag) { 137 | return stringBuilderParam.toString(); 138 | } 139 | return ""; 140 | } 141 | 142 | protected String formatSee(Method methodNode) { 143 | boolean hasTag = false; 144 | StringBuilder stringBuilderParam = new StringBuilder(); 145 | stringBuilderParam.append("
See Also:
"); 146 | for (TagInfo tagInfo : methodNode.getTag()) { 147 | if (!tagInfo.getName().equals("@see")) { 148 | continue; 149 | } 150 | hasTag = true; 151 | String camelCasedKeyword = tagInfo.getText(); 152 | int index = camelCasedKeyword.indexOf('#'); 153 | if (index >= 0) { 154 | camelCasedKeyword = camelCasedKeyword.substring(index + 1); 155 | } 156 | camelCasedKeyword = camelCasedKeyword.trim(); 157 | 158 | stringBuilderParam.append("    `"); 159 | char[] camelCasedKeywordArray = camelCasedKeyword.toCharArray(); 160 | stringBuilderParam.append(Character.toUpperCase(camelCasedKeywordArray[0])); 161 | for (int i = 1; i < camelCasedKeywordArray.length; i++) { 162 | if (camelCasedKeywordArray[i] >= 'A' && camelCasedKeywordArray[i] <= 'Z') { 163 | stringBuilderParam.append(' '); 164 | } 165 | stringBuilderParam.append(camelCasedKeywordArray[i]); 166 | } 167 | stringBuilderParam.append("`
"); 168 | } 169 | if (hasTag) { 170 | return stringBuilderParam.toString(); 171 | } 172 | return ""; 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/utils/Python.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.utils; 2 | 3 | import java.io.File; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public abstract class Python { 11 | 12 | public static String join(String glue, String[] strings) { 13 | return join(glue, Arrays.asList(strings)); 14 | } 15 | 16 | public static String join(String glue, Iterable strings) { 17 | if (strings == null) { 18 | return null; 19 | } 20 | 21 | StringBuilder stringBuilder = new StringBuilder(); 22 | String verkett = ""; 23 | for (String string : strings) { 24 | stringBuilder.append(verkett); 25 | stringBuilder.append(string); 26 | verkett = glue; 27 | } 28 | return stringBuilder.toString(); 29 | } 30 | 31 | public static Map zip(List keys, List values) { 32 | if (keys.size() != values.size()) { 33 | return null; 34 | } 35 | 36 | Map map = new HashMap(); 37 | Iterator valueIterator = values.listIterator(); 38 | for (A key : keys) { 39 | map.put(key, valueIterator.next()); 40 | } 41 | return map; 42 | } 43 | 44 | public static String osPathBasename(String path) { 45 | int index = path.lastIndexOf(File.separatorChar) + 1; 46 | return path.substring(index); 47 | } 48 | 49 | public static String osPathDirname(String path) { 50 | int index = path.lastIndexOf(File.separatorChar) + 1; 51 | String head = path.substring(0, index); 52 | if (head.length() != 0) { 53 | String regex = ""; 54 | if (File.separatorChar == '/') { 55 | regex = String.format("/{%d}", head.length()); 56 | } else { 57 | regex = String.format("\\\\{%d}", head.length()); 58 | } 59 | if (!head.matches(regex)) { 60 | head = rstrip(head, File.separatorChar); 61 | } 62 | } 63 | return head; 64 | } 65 | 66 | public static String[] osPathSplitDrive(String path) { 67 | String[] array = new String[2]; 68 | if (File.separatorChar == '/') { 69 | array[0] = ""; 70 | array[1] = path; 71 | } else { 72 | int index = path.indexOf(':') + 1; 73 | array[0] = path.substring(0, index); 74 | array[1] = path.substring(index); 75 | } 76 | return array; 77 | } 78 | 79 | public static String lstrip(String string) { 80 | return lstrip(string, ' '); 81 | } 82 | 83 | public static String lstrip(String string, char trimChar) { 84 | int stringLength = string.length(); 85 | 86 | int i; 87 | for (i = 0; i < stringLength && string.charAt(i) == trimChar; i++) { 88 | /* 89 | * Increment i until it is at the location of the first char that 90 | * does not match the trimChar given. 91 | */ 92 | } 93 | 94 | if (i == 0) { 95 | return string; 96 | } else { 97 | return string.substring(i); 98 | } 99 | } 100 | 101 | public static String rstrip(String string) { 102 | return rstrip(string, ' '); 103 | } 104 | 105 | public static String rstrip(String string, char trimChar) { 106 | int lastChar = string.length() - 1; 107 | 108 | int i; 109 | for (i = lastChar; i >= 0 && string.charAt(i) == trimChar; i--) { 110 | /* 111 | * Decrement i until it is equal to the first char that does not 112 | * match the trimChar given. 113 | */ 114 | } 115 | 116 | if (i < lastChar) { 117 | // the +1 is so we include the char at i 118 | return string.substring(0, i + 1); 119 | } else { 120 | return string; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/utils/Robotframework.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.utils; 2 | 3 | import java.io.File; 4 | import java.io.UnsupportedEncodingException; 5 | import java.util.Locale; 6 | 7 | import com.github.markusbernhardt.selenium2library.Selenium2LibraryNonFatalException; 8 | 9 | public abstract class Robotframework { 10 | 11 | public static String getLinkPath(File target, File base) { 12 | String path = getPathname(target, base); 13 | return encodeURLComponent(path); 14 | } 15 | 16 | public static String encodeURLComponent(final String s) { 17 | if (s == null) { 18 | return ""; 19 | } 20 | 21 | final StringBuilder sb = new StringBuilder(); 22 | try { 23 | for (int i = 0; i < s.length(); i++) { 24 | final char c = s.charAt(i); 25 | if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) 26 | || (c == '-') || (c == '.') || (c == '_') || (c == '~')) { 27 | sb.append(c); 28 | } else { 29 | final byte[] bytes = ("" + c).getBytes("UTF-8"); 30 | for (byte b : bytes) { 31 | sb.append('%'); 32 | 33 | int upper = (((int) b) >> 4) & 0xf; 34 | sb.append(Integer.toHexString(upper).toUpperCase(Locale.US)); 35 | 36 | int lower = ((int) b) & 0xf; 37 | sb.append(Integer.toHexString(lower).toUpperCase(Locale.US)); 38 | } 39 | } 40 | } 41 | 42 | return sb.toString(); 43 | } catch (UnsupportedEncodingException uee) { 44 | throw new Selenium2LibraryNonFatalException(uee); 45 | } 46 | } 47 | 48 | public static String getPathname(File target, File base) { 49 | String targetName = target.getAbsolutePath(); 50 | String baseName = base.getAbsolutePath(); 51 | if (base.isFile()) { 52 | baseName = Python.osPathDirname(baseName); 53 | } 54 | if (baseName.equals(targetName)) { 55 | return Python.osPathBasename(targetName); 56 | } 57 | 58 | String[] splittedBaseName = Python.osPathSplitDrive(baseName); 59 | if (!Python.osPathSplitDrive(targetName)[0].equals(splittedBaseName[0])) { 60 | return targetName; 61 | } 62 | 63 | int commonLen = commonPath(baseName, targetName).length(); 64 | if (splittedBaseName[1].equals(File.separator)) { 65 | return targetName.substring(commonLen); 66 | } 67 | 68 | if (commonLen == splittedBaseName[0].length() + File.separator.length()) { 69 | commonLen -= File.separator.length(); 70 | } 71 | 72 | baseName = baseName.substring(commonLen); 73 | StringBuilder builder = new StringBuilder(); 74 | int index = -1; 75 | while ((index = baseName.indexOf(File.separatorChar, index + 1)) != -1) { 76 | builder.append(".."); 77 | builder.append(File.separator); 78 | } 79 | builder.append(targetName.substring(commonLen + 1)); 80 | 81 | return builder.toString(); 82 | } 83 | 84 | public static String commonPath(String p1, String p2) { 85 | while (p1.length() > 0 && p2.length() > 0) { 86 | if (p1.equals(p2)) { 87 | return p1; 88 | } 89 | if (p1.length() > p2.length()) { 90 | p1 = Python.osPathDirname(p1); 91 | } else { 92 | p2 = Python.osPathDirname(p2); 93 | } 94 | } 95 | return ""; 96 | } 97 | 98 | public static String secsToTimestr(double double_secs) { 99 | TimestrHelper secsToTimestrHelper = new TimestrHelper(double_secs); 100 | return secsToTimestrHelper.getValue(); 101 | } 102 | 103 | public static double timestrToSecs(String timestr) { 104 | timestr = normalizeTimestr(timestr); 105 | if (timestr.length() == 0) { 106 | throw new Selenium2LibraryNonFatalException("Invalid timestr: " + timestr); 107 | } 108 | 109 | try { 110 | return Double.parseDouble(timestr); 111 | } catch (NumberFormatException nfe) { 112 | // Do nothing. No number. Try something else 113 | } 114 | 115 | int millis = 0; 116 | int secs = 0; 117 | int mins = 0; 118 | int hours = 0; 119 | int days = 0; 120 | int sign = 0; 121 | if (timestr.charAt(0) == '-') { 122 | sign = -1; 123 | timestr = timestr.substring(1); 124 | } else { 125 | sign = 1; 126 | } 127 | 128 | StringBuilder stringBuilder = new StringBuilder(); 129 | for (char c : timestr.toCharArray()) { 130 | switch (c) { 131 | case 'x': 132 | millis = Integer.parseInt(stringBuilder.toString()); 133 | stringBuilder = new StringBuilder(); 134 | break; 135 | case 's': 136 | secs = Integer.parseInt(stringBuilder.toString()); 137 | stringBuilder = new StringBuilder(); 138 | break; 139 | case 'm': 140 | mins = Integer.parseInt(stringBuilder.toString()); 141 | stringBuilder = new StringBuilder(); 142 | break; 143 | case 'h': 144 | hours = Integer.parseInt(stringBuilder.toString()); 145 | stringBuilder = new StringBuilder(); 146 | break; 147 | case 'p': 148 | days = Integer.parseInt(stringBuilder.toString()); 149 | stringBuilder = new StringBuilder(); 150 | break; 151 | default: 152 | stringBuilder.append(c); 153 | } 154 | } 155 | if (stringBuilder.length() != 0) { 156 | throw new Selenium2LibraryNonFatalException("Invalid timestr: " + timestr); 157 | } 158 | return sign * (millis / 1000 + secs + mins * 60 + hours * 60 * 60 + days * 60 * 60 * 24); 159 | } 160 | 161 | public static String normalizeTimestr(String timestr) { 162 | timestr = timestr.toLowerCase().replace(" ", ""); 163 | timestr = timestr.replace("milliseconds", "ms"); 164 | timestr = timestr.replace("millisecond", "ms"); 165 | timestr = timestr.replace("millis", "ms"); 166 | timestr = timestr.replace("seconds", "s"); 167 | timestr = timestr.replace("second", "s"); 168 | timestr = timestr.replace("secs", "s"); 169 | timestr = timestr.replace("sec", "s"); 170 | timestr = timestr.replace("minutes", "m"); 171 | timestr = timestr.replace("minute", "m"); 172 | timestr = timestr.replace("mins", "m"); 173 | timestr = timestr.replace("min", "m"); 174 | timestr = timestr.replace("hours", "h"); 175 | timestr = timestr.replace("hour", "h"); 176 | timestr = timestr.replace("days", "d"); 177 | timestr = timestr.replace("day", "d"); 178 | // 1) 'ms' -> 'x' to ease processing later 179 | // 2) 'd' -> 'p' because float('1d') returns 1.0 in Jython (bug 180 | // submitted) 181 | timestr = timestr.replace("ms", "x"); 182 | timestr = timestr.replace("d", "p"); 183 | return timestr; 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/utils/TimestrHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class TimestrHelper { 7 | 8 | protected boolean compact; 9 | protected List ret = new ArrayList(); 10 | protected String sign; 11 | protected int millis; 12 | protected int secs; 13 | protected int mins; 14 | protected int hours; 15 | protected int days; 16 | 17 | public TimestrHelper(double double_secs) { 18 | this(double_secs, false); 19 | } 20 | 21 | public TimestrHelper(double double_secs, boolean compact) { 22 | this.compact = compact; 23 | secsToComponents(double_secs); 24 | addItem(days, "d", "day"); 25 | addItem(hours, "h", "hour"); 26 | addItem(mins, "min", "minute"); 27 | addItem(secs, "s", "second"); 28 | addItem(millis, "ms", "millisecond"); 29 | } 30 | 31 | public String getValue() { 32 | if (ret.size() > 0) { 33 | return sign + Python.join(" ", ret); 34 | } 35 | return compact ? "0s" : "0 seconds"; 36 | } 37 | 38 | protected int doubleSecsToSecs(double double_secs) { 39 | return (int) double_secs; 40 | } 41 | 42 | protected int doubleSecsToMillis(double double_secs) { 43 | int int_secs = doubleSecsToSecs(double_secs); 44 | return (int) Math.round((double_secs - int_secs) * 1000); 45 | } 46 | 47 | protected void addItem(int value, String compactSuffix, String longSuffix) { 48 | if (value == 0) { 49 | return; 50 | } 51 | String suffix = compactSuffix; 52 | if (!compact) { 53 | suffix = String.format(" %s%s", longSuffix, pluralOrNot(value)); 54 | } 55 | ret.add(String.format("%d%s", value, suffix)); 56 | } 57 | 58 | protected void secsToComponents(double double_secs) { 59 | if (double_secs < 0) { 60 | sign = "- "; 61 | double_secs = Math.abs(double_secs); 62 | } else { 63 | sign = ""; 64 | } 65 | int int_secs = doubleSecsToSecs(double_secs); 66 | millis = doubleSecsToMillis(double_secs); 67 | secs = int_secs % 60; 68 | mins = int_secs / 60 % 60; 69 | hours = int_secs / (60 * 60) % 24; 70 | days = int_secs / (60 * 60 * 24); 71 | } 72 | 73 | protected String pluralOrNot(int value) { 74 | return value == 1 ? "" : "s"; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/github/markusbernhardt/selenium2library/utils/WebDriverCache.java: -------------------------------------------------------------------------------- 1 | package com.github.markusbernhardt.selenium2library.utils; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | import java.util.Stack; 6 | import java.util.TreeMap; 7 | 8 | import org.openqa.selenium.WebDriver; 9 | 10 | import com.github.markusbernhardt.selenium2library.Selenium2LibraryFatalException; 11 | 12 | public class WebDriverCache { 13 | 14 | /** 15 | * The currently active web driver instance 16 | */ 17 | SessionIdAliasWebDriverTuple currentSessionIdAliasWebDriverTuple; 18 | 19 | /** 20 | * The maximum assigned session id 21 | */ 22 | int maxAssignedSessionId = 0; 23 | 24 | /** 25 | * Stack of currently open session ids to reuse 26 | */ 27 | Stack openSessionIds = new Stack(); 28 | 29 | /** 30 | * Stack of already closed session ids to reuse 31 | */ 32 | Stack closedSessionIds = new Stack(); 33 | 34 | /** 35 | * Map session ids to webdrivers 36 | */ 37 | Map tupleBySessionId = new TreeMap(); 38 | 39 | /** 40 | * Map aliases to webdrivers 41 | */ 42 | Map tupleByAlias = new TreeMap(); 43 | 44 | public String register(WebDriver webDriver, String alias) { 45 | // create the new tuple 46 | currentSessionIdAliasWebDriverTuple = new SessionIdAliasWebDriverTuple(); 47 | currentSessionIdAliasWebDriverTuple.alias = alias; 48 | currentSessionIdAliasWebDriverTuple.webDriver = webDriver; 49 | if (closedSessionIds.size() == 0) { 50 | // no closed id 51 | maxAssignedSessionId++; 52 | currentSessionIdAliasWebDriverTuple.id = Integer.toString(maxAssignedSessionId); 53 | } else { 54 | // reuse closed id 55 | currentSessionIdAliasWebDriverTuple.id = closedSessionIds.pop(); 56 | } 57 | 58 | // store the new tuple 59 | openSessionIds.push(currentSessionIdAliasWebDriverTuple.id); 60 | tupleBySessionId.put(currentSessionIdAliasWebDriverTuple.id, currentSessionIdAliasWebDriverTuple); 61 | if (alias != null) { 62 | tupleByAlias.put(currentSessionIdAliasWebDriverTuple.alias, currentSessionIdAliasWebDriverTuple); 63 | } 64 | return currentSessionIdAliasWebDriverTuple.id; 65 | } 66 | 67 | public WebDriver getCurrent() { 68 | if (currentSessionIdAliasWebDriverTuple != null) { 69 | return currentSessionIdAliasWebDriverTuple.webDriver; 70 | } 71 | return null; 72 | } 73 | 74 | public String getCurrentSessionId() { 75 | if (currentSessionIdAliasWebDriverTuple != null) { 76 | return currentSessionIdAliasWebDriverTuple.id; 77 | } 78 | return null; 79 | } 80 | 81 | public void close() { 82 | if (currentSessionIdAliasWebDriverTuple != null) { 83 | // Close the webdriver and remove it from all stores 84 | currentSessionIdAliasWebDriverTuple.webDriver.quit(); 85 | tupleBySessionId.remove(currentSessionIdAliasWebDriverTuple.id); 86 | openSessionIds.remove(currentSessionIdAliasWebDriverTuple.id); 87 | closedSessionIds.push(currentSessionIdAliasWebDriverTuple.id); 88 | if (currentSessionIdAliasWebDriverTuple.alias != null) { 89 | tupleByAlias.remove(currentSessionIdAliasWebDriverTuple.alias); 90 | } 91 | 92 | // Set the last opened webdriver as current webdriver 93 | if (openSessionIds.size() != 0) { 94 | currentSessionIdAliasWebDriverTuple = tupleBySessionId.get(openSessionIds.pop()); 95 | } else { 96 | currentSessionIdAliasWebDriverTuple = null; 97 | } 98 | } 99 | } 100 | 101 | public void closeAll() { 102 | for (SessionIdAliasWebDriverTuple sessionIdAliasWebDriverTuple : tupleBySessionId.values()) { 103 | sessionIdAliasWebDriverTuple.webDriver.quit(); 104 | } 105 | maxAssignedSessionId = 0; 106 | currentSessionIdAliasWebDriverTuple = null; 107 | openSessionIds = new Stack(); 108 | closedSessionIds = new Stack(); 109 | tupleBySessionId = new TreeMap(); 110 | tupleByAlias = new TreeMap(); 111 | } 112 | 113 | public void switchBrowser(String sessionIdOrAlias) { 114 | SessionIdAliasWebDriverTuple sessionIdAliasWebDriverTuple = tupleByAlias.get(sessionIdOrAlias); 115 | if (sessionIdAliasWebDriverTuple != null) { 116 | currentSessionIdAliasWebDriverTuple = sessionIdAliasWebDriverTuple; 117 | openSessionIds.remove(currentSessionIdAliasWebDriverTuple.id); 118 | openSessionIds.push(currentSessionIdAliasWebDriverTuple.id); 119 | return; 120 | } 121 | 122 | sessionIdAliasWebDriverTuple = tupleBySessionId.get(sessionIdOrAlias); 123 | if (sessionIdAliasWebDriverTuple != null) { 124 | currentSessionIdAliasWebDriverTuple = sessionIdAliasWebDriverTuple; 125 | openSessionIds.remove(currentSessionIdAliasWebDriverTuple.id); 126 | openSessionIds.push(currentSessionIdAliasWebDriverTuple.id); 127 | return; 128 | } 129 | throw new Selenium2LibraryFatalException(String.format("Non-existing index or alias '%s'", sessionIdOrAlias)); 130 | } 131 | 132 | public Collection getWebDrivers() { 133 | return tupleBySessionId.values(); 134 | } 135 | 136 | public static class SessionIdAliasWebDriverTuple { 137 | public String id; 138 | public String alias; 139 | public WebDriver webDriver; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/resources/com/github/markusbernhardt/selenium2library/Selenium2Library.properties: -------------------------------------------------------------------------------- 1 | version=${project.version} 2 | -------------------------------------------------------------------------------- /src/test/robotframework/adapters/Selenium2Library.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Loads the Selenium2Library. 3 | Library Selenium2Library 4 | -------------------------------------------------------------------------------- /src/test/robotframework/imports/Google.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Defines which objects are available to 3 | ... all testcases for the Google website. 4 | Resource ../objects/Google.Search.txt 5 | Resource ../objects/Google.Site.txt 6 | Resource ../objects/Google.TopNavi.txt 7 | -------------------------------------------------------------------------------- /src/test/robotframework/objects/Google.Search.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Defines all required resources and keywords 3 | ... handling the google search. 4 | Resource ../adapters/Selenium2Library.txt 5 | 6 | *** Variables *** 7 | ${Google.Search.Locator.SearchField} id=lst-ib 8 | ${Google.Search.Locator.SearchResult.Rtomac} xpath=//a[contains(.,'MarkusBernhardt')] 9 | 10 | *** Keywords *** 11 | Search String 12 | [Arguments] ${Search.String} 13 | Input Text ${Google.Search.Locator.SearchField} ${Search.String} 14 | Press Key ${Google.Search.Locator.SearchField} \\13 15 | Wait Until Element Is Visible ${Google.Search.Locator.SearchResult.Rtomac} 16 | -------------------------------------------------------------------------------- /src/test/robotframework/objects/Google.Site.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Defines all required resources and keywords 3 | ... handling the google website. 4 | Resource ../adapters/Selenium2Library.txt 5 | 6 | *** Variables *** 7 | ${Google.Site.Url} http://www.google.de 8 | ${Google.Site.PageTitle} Google 9 | 10 | *** Keywords *** 11 | Open 12 | [Arguments] ${Browser.Alias}=default 13 | Run Keyword If '${Selenium.Grid}'!='true' Open Browser ${Google.Site.Url} ${Selenium.Browser.Name} ${Browser.Alias} 14 | Run Keyword If '${Selenium.Grid}'=='true' Open Browser ${Google.Site.Url} ${Selenium.Browser.Name} ${Browser.Alias} http://${Selenium.Host}:${Selenium.Port}/wd/hub browserName:${Selenium.Browser.Name},version:${Selenium.Browser.Version} 15 | Set Selenium Implicit Wait ${Selenium.Timeout} 16 | Set Selenium Timeout ${Selenium.Timeout} 17 | Google.Site.Init 18 | 19 | Init 20 | Delete All Cookies 21 | Google.Site.Go To 22 | 23 | Go To 24 | ${Current.Location}= Get Location 25 | Run Keyword If '${Current.Location}'!='${Google.Site.Url}' Selenium2Library.Go To ${Google.Site.Url} 26 | Title Should Be ${Google.Site.PageTitle} 27 | 28 | Close 29 | Close Browser 30 | -------------------------------------------------------------------------------- /src/test/robotframework/objects/Google.TopNavi.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Defines all required resources and keywords 3 | ... handling the google top navigation. 4 | Resource ../adapters/Selenium2Library.txt -------------------------------------------------------------------------------- /src/test/robotframework/settings/Settings.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation A resource file to setup all variables. 3 | Resource profiles/${Profile}.txt 4 | 5 | *** Variables *** 6 | ${Profile} Local 7 | ${Selenium.Browser.Name} firefox 8 | ${Selenium.Browser.Version} 17 9 | ${Selenium.Timeout} 30.0 10 | -------------------------------------------------------------------------------- /src/test/robotframework/settings/profiles/Jenkins.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation A resource file with variables specific for the 3 | ... execution of the testsuites by the Jenkins CI server. 4 | 5 | *** Variables *** 6 | ${Selenium.Grid} true 7 | ${Selenium.Host} h142845 8 | ${Selenium.Port} 5569 9 | -------------------------------------------------------------------------------- /src/test/robotframework/settings/profiles/Local.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation A resource file with variables specific for the local 3 | ... execution of the testsuites. 4 | 5 | *** Variables *** 6 | ${Selenium.Grid} false 7 | -------------------------------------------------------------------------------- /src/test/robotframework/testsuites/UnitTests/AW3Schools.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Suite Teardown Close All Browsers 3 | Library Selenium2Library 4 | 5 | *** Variables *** 6 | ${URL Application} http://www.w3schools.com 7 | 8 | *** Test Cases *** 9 | Select 10 | Open Browser http://www.w3schools.com firefox mainbrowser 11 | Go To http://www.w3schools.com/tags/tryit.asp?filename=tryhtml_select 12 | Wait Until Page Contains Element iframeResult 13 | Select Frame iframeResult 14 | Wait Until Page Contains Element xpath=//select 15 | Select From List xpath=//select Opel 16 | ${label} Get Selected List Label xpath=//select 17 | Should Be Equal ${label} Opel 18 | List Selection Should Be xpath=//select Opel 19 | 20 | Multiple Browsers 21 | Open Browser http://www.w3schools.com firefox mainbrowser1 22 | Open Browser http://www.w3schools.com firefox mainbrowser2 23 | Close All Browsers 24 | 25 | No Browser Alias 26 | Open Browser http://www.w3schools.com firefox 27 | Close Browser 28 | 29 | Close All Browsers reset the browser index to 1 30 | [Documentation] Ensure that the Browser count is reset to 0 when Close All Browsers is used. 31 | ${id} Open Browser http://www.w3schools.com firefox mainbrowser1 32 | Should Be Equal ${id} 1 33 | ${id} Open Browser http://www.w3schools.com firefox mainbrowser2 34 | Should Be Equal ${id} 2 35 | Close All Browsers 36 | ${id} Open Browser http://www.w3schools.com firefox mainbrowser1 37 | Should Be Equal ${id} 1 38 | Close All Browsers 39 | 40 | Open Browser after Close Browser creates unique browser id 41 | [Documentation] Ensure that the Close Browser keyword doesn't reset the count, rather that it decrements. 42 | ${id} Open Browser http://www.w3schools.com firefox mainbrowser1 43 | Should Be Equal ${id} 1 44 | ${id} Open Browser http://www.w3schools.com firefox mainbrowser2 45 | Should Be Equal ${id} 2 46 | Close browser 47 | ${id} Open Browser http://www.w3schools.com firefox mainbrowser2 48 | Should Be Equal ${id} 2 49 | ${id} Open Browser http://www.w3schools.com firefox mainbrowser3 50 | Should Be Equal ${id} 3 51 | Close browser 52 | ${id} Open Browser http://www.w3schools.com firefox mainbrowser4 53 | Should Be Equal ${id} 3 54 | Switch Browser 2 55 | Close browser 56 | ${id} Open Browser http://www.w3schools.com firefox mainbrowser2 57 | Should Be Equal ${id} 2 58 | Close All Browsers 59 | -------------------------------------------------------------------------------- /src/test/robotframework/testsuites/UnitTests/ExtJS.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Suite Setup Open Page 3 | Suite Teardown Close Browser 4 | Library Selenium2Library 5 | 6 | *** Variables *** 7 | ${URL Application} http://dev.sencha.com/deploy/ext-4.0.0/examples/writer/writer.html 8 | ${add} extJsButton=button[text='Add'] 9 | ${inputfield email} extJsTextfield=textfield[name='email'] 10 | 11 | *** Keywords *** 12 | Open Page 13 | Add Location Strategy extJs return window.document.getElementById((Ext.ComponentQuery.query(arguments[0])[0]).getId()); 14 | Add Location Strategy extJsButton return window.document.getElementById((Ext.ComponentQuery.query(arguments[0])[0]).btnEl.id); 15 | Add Location Strategy extJsTextfield return window.document.getElementById((Ext.ComponentQuery.query(arguments[0])[0]).getInputId()); 16 | Open Browser ${URL Application} firefox mainbrowser 17 | Log System Info 18 | Log Remote Session Id 19 | Log Remote Capabilities 20 | 21 | *** Test Cases *** 22 | Add Employee 23 | Click Button ${add} 24 | Capture Page Screenshot 25 | 26 | Input Text ${inputfield email} Haaalloooo Welt 27 | Capture Page Screenshot 28 | -------------------------------------------------------------------------------- /src/test/robotframework/testsuites/UnitTests/GetInnerElementId.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Library Selenium2Library 3 | 4 | *** Test Cases *** 5 | Get Inner Element Id test 6 | Open Browser https://www.google.co.in/#q=table+with+inner+element firefox mainbrowser 7 | ${innerelementid}= Get Inner Element Id //*[@id="viewport"] doc 0 8 | Log Variables 9 | Close Browser -------------------------------------------------------------------------------- /src/test/robotframework/testsuites/UnitTests/Google.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Suite Setup Google.Site.Open 3 | Suite Teardown Google.Site.Close 4 | Test Setup Google.Site.Init 5 | Resource ../../settings/Settings.txt 6 | Resource ../../imports/Google.txt 7 | 8 | *** Testcases *** 9 | Open And Close Google Site 10 | Capture Page Screenshot 11 | 12 | Store Web Element In JavaScript 13 | Execute Javascript window.document.my_element = window.document.getElementsByClassName('gsfi')[0]; 14 | ${className}= Execute Javascript return window.document.my_element.className; 15 | Should Contain ${className} gsfi 16 | 17 | Search Robotframework Selenium2Library 18 | Google.Search.Search String Robotframework Selenium2Library Java 19 | 20 | Search With JavaScript Locator 21 | Selenium2Library.Add Location Strategy elementById return window.document.getElementById(arguments[0]); 22 | Input Text elementById=lst-ib Robotframework Selenium2Library Java 23 | Press Key elementById=lst-ib \\13 24 | Wait Until Element Is Visible xpath=//a[contains(.,'MarkusBernhardt')] 25 | Capture Page Screenshot 26 | 27 | Search With JavaScript Locator Text 28 | Selenium2Library.Add Location Strategy text return window.document.evaluate("//*[contains(@value,'" + arguments[0] + "')]", window.document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; 29 | Wait Until Element Is Visible text=Google-Suche 30 | 31 | Search Without Locator Type 32 | Input Text lst-ib Robotframework Selenium2Library Java 33 | Press Key lst-ib \\13 34 | Wait Until Element Is Visible //a[contains(.,'MarkusBernhardt')] 35 | 36 | Get Id Of Active Element With JavaScript 37 | Input Text lst-ib Robotframework Selenium2Library 38 | ${activeElementId}= Execute JavaScript return window.document.activeElement.id; 39 | -------------------------------------------------------------------------------- /src/test/robotframework/testsuites/UnitTests/JSEvents.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Suite Setup Open Page 3 | Suite Teardown Close All Browsers 4 | Library Selenium2Library 5 | 6 | *** Variables *** 7 | ${URL Application} http://fiddle.jshell.net/ShPVX/show/ 8 | 9 | *** Keywords *** 10 | Open Page 11 | Open Browser ${URL Application} 12 | Select Frame xpath=//iframe 13 | Wait Until Page Contains Element xpath=//select[@name='test'] 14 | 15 | *** Test Cases *** 16 | Change dropdown value 17 | Select From List xpath=//select[@name='test'] 10 18 | Capture Page Screenshot 19 | Page Should Not Contain Element xpath=//div[@id='2'] 20 | Page Should Contain Element xpath=//div[@id='10'] -------------------------------------------------------------------------------- /src/test/robotframework/testsuites/UnitTests/WhatAreCookies.txt: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Library Selenium2Library 3 | 4 | *** Test Cases *** 5 | Cookies 6 | Open Browser http://www.whatarecookies.com/cookietest.asp firefox mainbrowser 7 | ${all_cookies}= Get Cookies 8 | ${test}= Get Cookie Value __atuvc 9 | Close Browser --------------------------------------------------------------------------------