├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── AUTHORS ├── Jenkinsfile ├── LICENSE ├── README.adoc ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main └── java │ └── io │ └── qameta │ └── htmlelements │ ├── WebPage.java │ ├── WebPageFactory.java │ ├── annotation │ ├── Description.java │ ├── FindBy.java │ ├── Name.java │ └── Param.java │ ├── context │ ├── Context.java │ ├── DefaultStore.java │ └── Store.java │ ├── element │ ├── Checkbox.java │ ├── ExtendedList.java │ ├── ExtendedWebElement.java │ ├── FileInput.java │ ├── HtmlElement.java │ ├── HtmlElementList.java │ ├── Image.java │ ├── Link.java │ └── ScrollableElement.java │ ├── exception │ ├── MethodInvocationException.java │ ├── NotImplementedException.java │ ├── WaitUntilException.java │ └── WebPageException.java │ ├── extension │ ├── ContextEnricher.java │ ├── ConvertMethod.java │ ├── DefaultMethod.java │ ├── DescriptionProvider.java │ ├── DriverProvider.java │ ├── ExtendWith.java │ ├── Extension.java │ ├── ExtensionRegistry.java │ ├── FilterMethod.java │ ├── HandleWith.java │ ├── HoverMethod.java │ ├── MethodHandler.java │ ├── MethodParameters.java │ ├── Retry.java │ ├── SelectorProvider.java │ ├── ShouldMethod.java │ ├── TargetModifier.java │ ├── Timeout.java │ ├── ToStringMethod.java │ ├── WaitUntilMethod.java │ ├── elements │ │ ├── ScrollMethod.java │ │ └── SetFileInputMethod.java │ └── page │ │ ├── BaseUrl.java │ │ ├── GoMethod.java │ │ └── IsAtMethod.java │ ├── handler │ └── WebBlockMethodHandler.java │ ├── matcher │ ├── DisplayedMatcher.java │ ├── HasTextMatcher.java │ └── PredicateMatcher.java │ ├── proxy │ └── Proxies.java │ ├── statement │ ├── Listener.java │ ├── ListenerStatement.java │ ├── RetryStatement.java │ ├── Statement.java │ └── StatementWrapper.java │ └── util │ ├── ReflectionUtils.java │ └── WebDriverUtils.java └── test └── java └── io └── qameta └── htmlelements ├── GlobalParametersTest.java ├── MatcherTest.java ├── WebPageTest.java └── example ├── TestData.java ├── element ├── SearchArrow.java ├── SearchForm.java ├── SuggestItem.java └── WithSuggest.java ├── page └── SearchPage.java └── scroll └── WikiMainPage.java /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | 3 | #IDEA Files 4 | .idea 5 | *.iml 6 | *.ipr 7 | 8 | #Eclipse files 9 | *.settings 10 | *.classpath 11 | *.project 12 | *.pydevproject -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eroshenkoam/htmlelements/25e4ce3227cd8f36204231c5cf832c11bd6ba1c2/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | The following authors have created the source code of "Html Elements" 2 | published and distributed by Artem Eroshenko as the owner: 3 | 4 | * Artem Eroshenko -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { label 'java' } 3 | parameters { 4 | booleanParam(name: 'RELEASE', defaultValue: false, description: 'Perform release?') 5 | string(name: 'RELEASE_VERSION', defaultValue: '', description: 'Release version') 6 | string(name: 'NEXT_VERSION', defaultValue: '', description: 'Next version (without SNAPSHOT)') 7 | } 8 | stages { 9 | stage('Build') { 10 | steps { 11 | sh './mvnw -Dmaven.test.failure.ignore=true clean verify' 12 | } 13 | } 14 | stage('Release') { 15 | when { expression { return params.RELEASE } } 16 | steps { 17 | configFileProvider([configFile(fileId: 'qameta-sonatype-settings.xml', variable: 'SETTINGS')]) { 18 | sshagent(['qameta-ci_ssh']) { 19 | sh 'git checkout master && git pull origin master' 20 | sh "./mvnw release:prepare release:perform -B -s ${env.SETTINGS} " + 21 | "-DreleaseVersion=${params.RELEASE_VERSION} " + 22 | "-DdevelopmentVersion=${params.NEXT_VERSION}-SNAPSHOT" 23 | } 24 | } 25 | } 26 | } 27 | } 28 | post { 29 | always { 30 | deleteDir() 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Artem Eroshenko 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Html Elements 2 | version: 2.0 3 | 4 | Second generation of the HtmlElements library is indeed a yet another WebDriver wrapper, but 5 | with a couple of fresh ideas: 6 | 7 | . Hierarchy of page objects for your application is now a very flexible composition of java interfaces. 8 | . A lot of additional control over elements behaviour is available via extension methods implemented in a fluent manner. 9 | . Actually, pretty much everything is extensible for your convenience: the library provides a baseline design 10 | for your project, allowing you to fine-tune the specifics. 11 | + 12 | 13 | == Overview 14 | HtmlElements 2 has several basic entities that you will be working with: 15 | 16 | *WebPage* - instances of this class will serve as top classes of your page objects hierarchy. 17 | 18 | *WebPageFactory* - returns WebPage instances that are wrapped around the given driver. 19 | [source, java] 20 | ---- 21 | WebPageFactory factory = new WebPageFactory(); 22 | SearchPage page = factory.get(driver, SearchPage.class); 23 | ---- 24 | 25 | *ExtendedWebElement* - is a parent interface for every web element you will be using in your tests. 26 | 27 | *ExtendedList* - an interface, which provides everything to work with a collection of ExtendedWebElements. 28 | 29 | == Structuring Page Objects 30 | 31 | [cols="2",frame="none"] 32 | |========================================================= 33 | 34 | a| 35 | 1) Defining a page: pages are top-classes and they may contain elements or whole element blocks. 36 | [source, java] 37 | ---- 38 | @BaseUrl("http://www.base.url/search") 39 | public interface SearchPage extends WebPage { 40 | 41 | @FindBy("//form[contains(@class, 'search2 suggest2-form')]") 42 | SearchArrow searchArrow(); 43 | 44 | @FindBy("//div[@class='toolbar']") 45 | Toolbar toolbar(); 46 | 47 | @FindBy("//div[@class='user-tooltip']") 48 | UserTooltip userTooltip(); 49 | } 50 | ---- 51 | a| 52 | Web pages usually contain some high-level blocks, that we will neatly organize via a composition 53 | [source, html] 54 | ---- 55 | 56 |
...
57 |
...
58 | ... 59 | 60 | ---- 61 | a| 62 | 2) Organizing a block of web elements on the page: you need to extend an interface from ExtendedWebElement 63 | and you are good to go 64 | [source, java] 65 | ---- 66 | public interface Toolbar extends ExtendedWebElement{ 67 | 68 | @FindBy(".//span[@class = 'status-text']") 69 | ExtendedWebElement statusText(); 70 | } 71 | ---- 72 | a| 73 | Your classes for such blocks of elements can contain sub-blocks, or once you've reached a singular element, 74 | use the ExtendedWebElement as it's type. 75 | [source, html] 76 | ---- 77 | 78 |
79 | Status 80 | ... 81 |
82 |
...
83 | ... 84 | 85 | ---- 86 | a| 87 | 3) You can define a collection of elements for a page or for a block 88 | [source, java] 89 | ---- 90 | public interface Toolbar extends ExtendedWebElement{ 91 | 92 | @FindBy(".//button[@class = 'action-button']") 93 | ExtendedList buttons(); 94 | 95 | @FindBy(".//span[@class = 'status-text']") 96 | ExtendedWebElement statusText(); 97 | } 98 | ---- 99 | a| 100 | Button elements have a specific ToolbarButton class to represent them. 101 | [source, html] 102 | ---- 103 | 104 |
105 | Status 106 | 107 | 111 | 115 | ... 116 |
117 |
...
118 | ... 119 | 120 | ---- 121 | a| 122 | 4) You can use parameterized selectors to simplify your work with homogeneous elements 123 | [source, java] 124 | ---- 125 | public interface SearchResultsPanel extends ExtendedWebElement { 126 | 127 | @Description("Search result for user {{userName}}") 128 | @FindBy(".//div[contains(@class, 'search-result-item') and contains(., {{userName}}]") 129 | UserResult user(@Param("userName") String userName); 130 | } 131 | ---- 132 | 133 | Invocation of parameterized method: 134 | [source, java] 135 | ---- 136 | UserResult foundUser = searchPage.resultsPanel().user("User 1"); 137 | ---- 138 | a| 139 | Here parameterized element will match a block of elements for User 1 from search results. Parameters can be used with 140 | collections of elements as well. 141 | [source, html] 142 | ---- 143 | 144 |
...
145 |
...
146 |
147 |
148 | User 1 149 | ... 150 |
151 |
152 | Task 1 153 | ... 154 |
155 |
156 | ... 157 | 158 | ---- 159 | a| 160 | 5) Very often you will have some identical html blocks across different pages, e.g. toolbars, footers, dropdowns, 161 | pickers e.t.c. Once you wrote a class describing such a block, it can be easily reused across all of the pages: 162 | [source, java] 163 | ---- 164 | 165 | @BaseUrl("http://www.base.url/search") 166 | public interface SearchPage extends WebPage, WithToolbar { 167 | 168 | //other elements for search page here 169 | } 170 | 171 | @BaseUrl("http://www.base.url/account") 172 | public interface AccountPage extends WebPage, WithToolbar { 173 | 174 | //other elements for account page here 175 | } 176 | 177 | public interface WithToolbar extends ExtendedWebElement{ 178 | 179 | @FindBy("//div[@class='toolbar']") 180 | Toolbar toolbar(); 181 | } 182 | ---- 183 | a| 184 | First page - base.url/search: 185 | [source, html] 186 | ---- 187 | 188 |
...
189 | ... 190 | 191 | ---- 192 | 193 | Second page - base.url/account: 194 | [source, html] 195 | ---- 196 | 197 |
...
198 | ... 199 | 200 | ---- 201 | 202 | both pages have a toolbar block, represented by the Toolbar class. 203 | |========================================================= 204 | 205 | == Features 206 | After you have described web pages, it's time to start organizing the logic of working with them. 207 | We prefer to use a BDD approach, where you break down all the interaction logic into simple actions and create 208 | one "step" method per action as it's implementation. That is where all the extension methods from ExtendedWebElement 209 | and ExtendedList classes come in handy. 210 | 211 | We will use http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html[harmcrest matchers] for conditioning 212 | 213 | 214 | === Working with elements 215 | *Waiting on a condition for a single element.* 216 | `ExtendedWebElement` have two different extension methods for waiting. 217 | 218 | First - `waitUntil(Matcher matcher)` which waits with a configurable timeout and polling on some condition 219 | in a passed matcher, throwing `java.lang.RuntimeException` if condition has not been satisfied. 220 | [source, java] 221 | ---- 222 | @Step("Make search with input string «{}»") 223 | public SearchPageSteps makeSearch(String input){ 224 | final SearchForm form = onSearchPage().searchPanel().form(); 225 | form.waitUntil(WebElement::isDisplayed) //waits for element to satisfy a condition 226 | .sendKeys(input); //invokes standard WebElement's method 227 | form.submit(); 228 | return this; 229 | } 230 | ---- 231 | 232 | Second - `should(Matcher matcher)` which waits the same way on a passed matcher, but throwing 233 | `java.lang.AssertionError` instead. 234 | [source, java] 235 | ---- 236 | @Step("Check user «{}» is found") 237 | public SearchPageSteps checkUserIsFound(String userName){ 238 | onSearchPage().resultsPanel().user(userName) 239 | .should("User is not found", WebElement::isDisplayed); 240 | //makes a waiting assertion here 241 | return this; 242 | } 243 | ---- 244 | 245 | *Performing actions on elements.* There're not many built-in extension methods for actions, since it would clutter the 246 | `WebElement` 's api too much. Instead it's better to introduce your own custom methods only when you actually need them. 247 | To show how this idea works we made a `ScrollableElement` and an additional `hover()` method for the `ExtendedWebElement`. 248 | 249 | [source, java] 250 | ---- 251 | @Step("Check current user info tooltip is shown") 252 | public SearchPageSteps checkUserInfoTooltip(String userName){ 253 | onSearchPage().toolbar().currentUserIcon() 254 | .hover(); //invokes new Actions().moveToElement(element) 255 | onSearchPage().userTooltip() 256 | .should("A tooltip should be displayed for user's avatar", Matchers::isDisplayed) 257 | //making first assertion that tooltip is shown 258 | .userName() // going to the child element in the same chain of calls 259 | .should("Unexpected user in tooltip", hasText(input)) //second assertion 260 | return this; 261 | } 262 | ---- 263 | 264 | `ScrollableElement` is just a very simple class introducing single `scrollToElement()` method. 265 | Annotation `@ScrollMethod` points to the method's implementation. 266 | [source, java] 267 | ---- 268 | public interface ScrollableElement extends ExtendedWebElement { 269 | 270 | @ScrollMethod 271 | void scrollToElement(); 272 | } 273 | ---- 274 | 275 | Now to perform a scroll you just need to specify it as a type for an element. 276 | [source, java] 277 | ---- 278 | public interface Footer extends ScrollableElement