From 39862e54ba4be44d96ac6b6c9f43dced73ce39c0 Mon Sep 17 00:00:00 2001 From: Pavlo Strunkin Date: Mon, 2 Nov 2026 22:51:26 +0200 Subject: [PATCH 1/3] chore: drop the JDI and Allure examples, one layout for the rest - remove jdi-light-testng and allure-junit5-gradle-kts with their CI jobs and Dependabot entries - every example reads vrt.json or VRT_* through the sdk-java config builder and ships vrt.json.example and a README; vrt.json is ignored - the standalone client takes the folder as its only argument - maven-compiler-plugin 3.16.0 (supersedes #26); everything else was already current - Apache-2.0 LICENSE, Dependabot open-pull-requests-limit 5 Co-Authored-By: Claude Opus 5.5 --- .github/dependabot.yml | 6 +- .github/workflows/ci.yml | 18 +- .gitignore | 3 +- LICENSE | 201 +++++++++ README.md | 23 +- allure-junit5-gradle-kts/.gitignore | 3 - allure-junit5-gradle-kts/build.gradle.kts | 25 -- .../gradle/wrapper/gradle-wrapper.jar | Bin 48749 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 9 - allure-junit5-gradle-kts/gradlew | 248 ----------- allure-junit5-gradle-kts/gradlew.bat | 112 ----- allure-junit5-gradle-kts/settings.gradle.kts | 1 - .../examples/junit5/AllureImageDiffTest.java | 77 ---- appium-maven/README.md | 31 ++ appium-maven/pom.xml | 2 +- .../test/java/IOSBasicInteractionsTest.java | 20 +- appium-maven/vrt.json.example | 7 + jdi-light-testng/.gitignore | 11 - jdi-light-testng/pom.xml | 104 ----- .../mytests/uiobjects/example/TestData.java | 405 ------------------ .../uiobjects/example/custom/Avatar.java | 20 - .../uiobjects/example/custom/MarvelUser.java | 13 - .../uiobjects/example/entities/Contacts.java | 9 - .../uiobjects/example/entities/Defaults.java | 13 - .../example/entities/LeftMenuData.java | 25 -- .../example/entities/MarvelUserInfo.java | 7 - .../uiobjects/example/entities/User.java | 9 - .../uiobjects/example/site/SiteJdi.java | 31 -- .../example/site/custom/MenuItem.java | 11 - .../example/site/custom/MultiDropdown.java | 252 ----------- .../example/site/custom/MyCheckList.java | 12 - .../example/site/pages/ContactFormPage.java | 12 - .../example/site/pages/ContactsPage.java | 25 -- .../example/site/pages/DatesPage.java | 10 - .../example/site/pages/HomePage.java | 9 - .../example/site/pages/Html5Page.java | 10 - .../site/pages/JDIPerformancePage.java | 19 - .../example/site/pages/UsersPage.java | 23 - .../example/site/sections/ContactForm.java | 33 -- .../java/org/mytests/tests/TestsInit.java | 26 -- .../tests/example/DataTableInteractTests.java | 50 --- .../java/org/mytests/tests/states/States.java | 42 -- .../tests/testng/RetryFailedTestCases.java | 22 - .../tests/testng/RetryListenerClass.java | 15 - .../mytests/tests/testng/TestNGListener.java | 69 --- .../services/org.testng.ITestNGListener | 1 - .../src/test/resources/chrome.properties | 2 - .../src/test/resources/log4j2.xml | 61 --- .../src/test/resources/test.properties | 33 -- playwright/README.md | 29 ++ playwright/pom.xml | 2 +- .../src/test/java/PlaywrightExampleTest.java | 19 +- playwright/vrt.json.example | 7 + selenide-gradle/README.md | 28 ++ .../src/test/java/SelenideExample.java | 21 +- selenide-gradle/vrt.json.example | 7 + selenium-maven/README.md | 28 ++ selenium-maven/pom.xml | 2 +- .../com/example/test/SeleniumExampleTest.java | 19 +- selenium-maven/vrt.json.example | 7 + standalone-from-folder-maven/.gitignore | 1 + standalone-from-folder-maven/README.md | 37 +- standalone-from-folder-maven/pom.xml | 2 +- .../src/main/java/CompareFromFile.java | 64 +-- standalone-from-folder-maven/vrt.json.example | 7 + 65 files changed, 470 insertions(+), 1980 deletions(-) create mode 100644 LICENSE delete mode 100644 allure-junit5-gradle-kts/.gitignore delete mode 100644 allure-junit5-gradle-kts/build.gradle.kts delete mode 100644 allure-junit5-gradle-kts/gradle/wrapper/gradle-wrapper.jar delete mode 100644 allure-junit5-gradle-kts/gradle/wrapper/gradle-wrapper.properties delete mode 100755 allure-junit5-gradle-kts/gradlew delete mode 100755 allure-junit5-gradle-kts/gradlew.bat delete mode 100644 allure-junit5-gradle-kts/settings.gradle.kts delete mode 100644 allure-junit5-gradle-kts/src/test/java/io/qameta/allure/examples/junit5/AllureImageDiffTest.java create mode 100644 appium-maven/README.md create mode 100644 appium-maven/vrt.json.example delete mode 100644 jdi-light-testng/.gitignore delete mode 100644 jdi-light-testng/pom.xml delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/TestData.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/custom/Avatar.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/custom/MarvelUser.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/Contacts.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/Defaults.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/LeftMenuData.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/MarvelUserInfo.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/User.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/SiteJdi.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MenuItem.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MultiDropdown.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MyCheckList.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/ContactFormPage.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/ContactsPage.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/DatesPage.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/HomePage.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/Html5Page.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/JDIPerformancePage.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/UsersPage.java delete mode 100644 jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/sections/ContactForm.java delete mode 100644 jdi-light-testng/src/test/java/org/mytests/tests/TestsInit.java delete mode 100644 jdi-light-testng/src/test/java/org/mytests/tests/example/DataTableInteractTests.java delete mode 100644 jdi-light-testng/src/test/java/org/mytests/tests/states/States.java delete mode 100644 jdi-light-testng/src/test/java/org/mytests/tests/testng/RetryFailedTestCases.java delete mode 100644 jdi-light-testng/src/test/java/org/mytests/tests/testng/RetryListenerClass.java delete mode 100644 jdi-light-testng/src/test/java/org/mytests/tests/testng/TestNGListener.java delete mode 100644 jdi-light-testng/src/test/resources/META-INF/services/org.testng.ITestNGListener delete mode 100644 jdi-light-testng/src/test/resources/chrome.properties delete mode 100644 jdi-light-testng/src/test/resources/log4j2.xml delete mode 100644 jdi-light-testng/src/test/resources/test.properties create mode 100644 playwright/README.md create mode 100644 playwright/vrt.json.example create mode 100644 selenide-gradle/README.md create mode 100644 selenide-gradle/vrt.json.example create mode 100644 selenium-maven/README.md create mode 100644 selenium-maven/vrt.json.example create mode 100644 standalone-from-folder-maven/vrt.json.example diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4f5da55..d0b4fdb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,12 +3,12 @@ updates: - package-ecosystem: maven directories: - "/appium-maven" - - "/jdi-light-testng" - "/playwright" - "/selenium-maven" - "/standalone-from-folder-maven" schedule: interval: weekly + open-pull-requests-limit: 5 groups: maven-minor-patch: patterns: @@ -28,10 +28,10 @@ updates: - package-ecosystem: gradle directories: - - "/allure-junit5-gradle-kts" - "/selenide-gradle" schedule: interval: weekly + open-pull-requests-limit: 5 groups: gradle-minor-patch: patterns: @@ -53,6 +53,7 @@ updates: directory: "/.github/vrt" schedule: interval: weekly + open-pull-requests-limit: 5 groups: docker: patterns: @@ -62,6 +63,7 @@ updates: directory: "/" schedule: interval: weekly + open-pull-requests-limit: 5 groups: github-actions: patterns: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f1f31f..85e9c9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,10 +19,16 @@ jobs: strategy: fail-fast: false matrix: - example: [jdi-light-testng, playwright, selenium-maven] + example: [playwright, selenium-maven] defaults: run: working-directory: ${{ matrix.example }} + env: + VRT_APIURL: http://localhost:4200 + VRT_APIKEY: DEFAULTUSERAPIKEYTOBECHANGED + VRT_PROJECT: Default project + VRT_BRANCHNAME: master + VRT_ENABLESOFTASSERT: "true" steps: - uses: actions/checkout@v7 - name: Start Visual Regression Tracker @@ -31,7 +37,7 @@ jobs: - uses: actions/setup-java@v6 with: distribution: temurin - java-version: ${{ matrix.example == 'jdi-light-testng' && 21 || 17 }} + java-version: 17 cache: maven cache-dependency-path: ${{ matrix.example }}/pom.xml - run: mvn -B test-compile @@ -45,10 +51,16 @@ jobs: strategy: fail-fast: false matrix: - example: [allure-junit5-gradle-kts, selenide-gradle] + example: [selenide-gradle] defaults: run: working-directory: ${{ matrix.example }} + env: + VRT_APIURL: http://localhost:4200 + VRT_APIKEY: DEFAULTUSERAPIKEYTOBECHANGED + VRT_PROJECT: Default project + VRT_BRANCHNAME: master + VRT_ENABLESOFTASSERT: "true" steps: - uses: actions/checkout@v7 - name: Start Visual Regression Tracker diff --git a/.gitignore b/.gitignore index 62c8935..36649a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.idea/ \ No newline at end of file +.idea/ +vrt.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 16a08cc..b3f338a 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,15 @@ # Visual Regression Tracker examples for Java -Each folder is a separate project on [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java) 5.5.0 from JitPack (Java 17; JDI Light needs Java 21): +Each folder is a separate project with its own build, `vrt.json.example` and README, on [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java) 5.5.0 from JitPack and Java 17 or later. The browser examples open https://the-internet.herokuapp.com headless and track screenshots in a [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker) 6.0 server. -- [Playwright + Maven + JUnit 4](playwright) -- [Selenium 4 + Maven + JUnit 4](selenium-maven) -- [Selenide + Gradle + TestNG](selenide-gradle) -- [Allure + Selenide + Gradle (Kotlin DSL) + JUnit 5](allure-junit5-gradle-kts) -- [Standalone Java: compare PNG and PDF files from a folder + Maven](standalone-from-folder-maven) -- [JDI Light + TestNG + Maven](jdi-light-testng) -- [Appium (iOS) + TestNG + Maven](appium-maven) +| Example | Stack | Client | +| ------------------------------------------------------------ | ---------------------------------------------------- | ----------------------------------------------------------------- | +| [playwright](playwright) | Playwright, Maven, JUnit 4 | [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java) | +| [selenium-maven](selenium-maven) | Selenium 4, Maven, JUnit 4 | [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java) | +| [selenide-gradle](selenide-gradle) | Selenide, Gradle, TestNG | [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java) | +| [appium-maven](appium-maven) | Appium on an iOS simulator, Maven, TestNG | [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java) | +| [standalone-from-folder-maven](standalone-from-folder-maven) | Command line: PNG and PDF files from a folder, Maven | [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java) | -The browser examples open https://the-internet.herokuapp.com headless. They connect to -`http://localhost:4200` with the default API key and project of a fresh -[Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker) -install; change that in each example's `VisualRegressionTrackerConfig`. +The examples connect with `vrt.json` or `VRT_*` environment variables, the way [sdk-java reads its configuration](https://github.com/Visual-Regression-Tracker/sdk-java#configuration). `vrt.json.example` holds the URL, API key and project of a fresh local install; copy it to `vrt.json` and change what differs. -Run a Maven example with `mvn test`, a Gradle one with `./gradlew test`. +CI runs the browser examples against a VRT server started from [.github/vrt/docker-compose.yml](.github/vrt/docker-compose.yml) and compiles the other two. diff --git a/allure-junit5-gradle-kts/.gitignore b/allure-junit5-gradle-kts/.gitignore deleted file mode 100644 index be9f372..0000000 --- a/allure-junit5-gradle-kts/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.gradle/ -build/ -.allure/ diff --git a/allure-junit5-gradle-kts/build.gradle.kts b/allure-junit5-gradle-kts/build.gradle.kts deleted file mode 100644 index bb27893..0000000 --- a/allure-junit5-gradle-kts/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -plugins { - java - id("io.qameta.allure") version "4.3.0" -} - -repositories { - mavenCentral() - maven(url = "https://jitpack.io") -} - -tasks.withType().configureEach { - options.release = 17 -} - -dependencies { - testImplementation("com.github.Visual-Regression-Tracker:sdk-java:5.5.0") - testImplementation("com.codeborne:selenide:7.19.1") - testImplementation(platform("org.junit:junit-bom:6.1.3")) - testImplementation("org.junit.jupiter:junit-jupiter") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} - -tasks.test { - useJUnitPlatform() -} diff --git a/allure-junit5-gradle-kts/gradle/wrapper/gradle-wrapper.jar b/allure-junit5-gradle-kts/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 9fa7f288283f81eec4952784c271abd7e0938dee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48749 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/allure-junit5-gradle-kts/gradlew.bat b/allure-junit5-gradle-kts/gradlew.bat deleted file mode 100755 index 03754c4..0000000 --- a/allure-junit5-gradle-kts/gradlew.bat +++ /dev/null @@ -1,112 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem gradlew startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables, and ensure extensions are enabled -setlocal EnableExtensions - -@rem Catch executions from older scripts and ensure they exit cleanly. -@rem This can be removed once we can be reasonably confident that few people -@rem will be migrating directly to this new wrapper. -goto afterSafetyNet -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -goto exitWithErrorLevel -:afterSafetyNet - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -1>&2 echo. -1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -1>&2 echo. -1>&2 echo Please set the JAVA_HOME variable in your environment to match the -1>&2 echo location of your Java installation. - -"%COMSPEC%" /c exit 1 -goto exitWithErrorLevel - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -1>&2 echo. -1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -1>&2 echo. -1>&2 echo Please set the JAVA_HOME variable in your environment to match the -1>&2 echo location of your Java installation. - -"%COMSPEC%" /c exit 1 -goto exitWithErrorLevel - -:execute -@rem Setup the command line - - - -@rem Execute gradlew -@rem endlocal doesn't take effect until after the line is parsed and variables are expanded -@rem which allows us to clear the local environment before executing the java command -endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel & goto exitWithErrorLevel - -@rem This label must not be changed. We rely on old scripts being able to jump to this point. -:exitWithErrorLevel -@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts -"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/allure-junit5-gradle-kts/settings.gradle.kts b/allure-junit5-gradle-kts/settings.gradle.kts deleted file mode 100644 index e63bc8b..0000000 --- a/allure-junit5-gradle-kts/settings.gradle.kts +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "allure-junit5-gradle-kts" diff --git a/allure-junit5-gradle-kts/src/test/java/io/qameta/allure/examples/junit5/AllureImageDiffTest.java b/allure-junit5-gradle-kts/src/test/java/io/qameta/allure/examples/junit5/AllureImageDiffTest.java deleted file mode 100644 index bbee2a7..0000000 --- a/allure-junit5-gradle-kts/src/test/java/io/qameta/allure/examples/junit5/AllureImageDiffTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.qameta.allure.examples.junit5; - -import com.codeborne.selenide.Configuration; -import com.codeborne.selenide.Selenide; -import io.qameta.allure.Allure; -import io.qameta.allure.Attachment; -import io.qameta.allure.Step; -import io.visual_regression_tracker.sdk_java.TestRunResult; -import io.visual_regression_tracker.sdk_java.TestRunStatus; -import io.visual_regression_tracker.sdk_java.VisualRegressionTracker; -import io.visual_regression_tracker.sdk_java.VisualRegressionTrackerConfig; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.openqa.selenium.OutputType; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; - -import static org.junit.jupiter.api.Assertions.assertNotEquals; - -public class AllureImageDiffTest { - - static VisualRegressionTrackerConfig config = VisualRegressionTrackerConfig.builder() - .apiUrl("http://localhost:4200") - .apiKey("DEFAULTUSERAPIKEYTOBECHANGED") - .project("Default project") - .branchName("master") - .enableSoftAssert(true) - .build(); - static VisualRegressionTracker vrt = new VisualRegressionTracker(config); - - @BeforeAll - static void setUp() throws IOException, InterruptedException { - Configuration.baseUrl = "https://the-internet.herokuapp.com"; - Configuration.browser = "chrome"; - Configuration.browserSize = "1280x1024"; - Configuration.headless = true; - - vrt.start(); - } - - @AfterAll - static void tearDown() throws IOException, InterruptedException { - Selenide.closeWebDriver(); - vrt.stop(); - } - - @Step - @Attachment(value = "{filename}", type = "image/png") - public static byte[] attachScreenshotToAllureReport(String href, String filename) { - if (href == null) { - return new byte[0]; - } - try (InputStream image = URI.create(href).toURL().openStream()) { - return image.readAllBytes(); - } catch (IOException e) { - return new byte[0]; - } - } - - @Test - @DisplayName("imageDiffExample displayName") - public void imageDiffExample() throws IOException, InterruptedException { - Selenide.open("/"); - - TestRunResult result = vrt.track("Home page", Selenide.screenshot(OutputType.BASE64)); - - Allure.label("testType", "screenshotDiff"); - attachScreenshotToAllureReport(result.getImageUrl(), "actual"); - attachScreenshotToAllureReport(result.getBaselineUrl(), "expected"); - attachScreenshotToAllureReport(result.getDiffUrl(), "diff"); - assertNotEquals(TestRunStatus.UNRESOLVED, result.getTestRunResponse().getStatus()); - } -} diff --git a/appium-maven/README.md b/appium-maven/README.md new file mode 100644 index 0000000..e07c033 --- /dev/null +++ b/appium-maven/README.md @@ -0,0 +1,31 @@ +# Appium (iOS) + Maven + TestNG + +[IOSBasicInteractionsTest.java](src/test/java/IOSBasicInteractionsTest.java) opens six screens of the sample app in [App.zip](src/main/resources/App.zip) on an iOS simulator with the [Appium Java client](https://github.com/appium/java-client) and tracks each with [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java). A screenshot that comes back `Unresolved` is taken again up to two times. [BaseTest.java](src/test/java/BaseTest.java) starts a local Appium server for the suite. + +CI only compiles this example: it needs macOS with an iOS simulator. + +## Prerequisites + +- macOS with Xcode and the simulator named in the test (`iPhone 12`, iOS 14.5); change `deviceName` and `platformVersion` to one you have +- [Appium](https://appium.io/docs/en/latest/quickstart/) with the XCUITest driver: `npm install -g appium`, then `appium driver install xcuitest` +- JDK 17 or later and Maven +- A running [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker) 6.0 server and its API key + +## Setup + +- Copy `vrt.json.example` to `vrt.json` and set your connection, or use the [`VRT_*` environment variables](https://github.com/Visual-Regression-Tracker/sdk-java#configuration), which override the file + +## Run + +```sh +mvn test +``` + +## What to expect + +`enableSoftAssert` is on: the SDK logs a failed check through SLF4J instead of throwing, so the test passes. The examples have no SLF4J binding, so the log stays silent; add one, such as `slf4j-simple`, to see it. + +- First run: the VRT UI shows a build with six new test runs, one per screen, from "Home page" to "Drag page". Approve them to make them the baseline for the branch. +- Next runs: the test runs are `Ok` while the screens look the same. When one changes, it is `Unresolved`, and the build shows the diff to approve or reject. + +Set `enableSoftAssert` to `false` to make new and changed screenshots fail the test. diff --git a/appium-maven/pom.xml b/appium-maven/pom.xml index 9854cd5..157a607 100644 --- a/appium-maven/pom.xml +++ b/appium-maven/pom.xml @@ -54,7 +54,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.15.0 + 3.16.0 org.apache.maven.plugins diff --git a/appium-maven/src/test/java/IOSBasicInteractionsTest.java b/appium-maven/src/test/java/IOSBasicInteractionsTest.java index b1ee65f..433448e 100644 --- a/appium-maven/src/test/java/IOSBasicInteractionsTest.java +++ b/appium-maven/src/test/java/IOSBasicInteractionsTest.java @@ -12,6 +12,7 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.io.File; import java.io.IOException; import java.time.Duration; import java.util.Objects; @@ -33,15 +34,7 @@ public void setUp() throws IOException, InterruptedException { driver = new IOSDriver(getServiceUrl(), options); - VisualRegressionTrackerConfig vrtConfig = VisualRegressionTrackerConfig.builder() - .apiUrl("http://localhost:4200") - .apiKey("DEFAULTUSERAPIKEYTOBECHANGED") - .project("Default project") - .branchName("master") - .enableSoftAssert(true) - .build(); - - vrt = new VisualRegressionTracker(vrtConfig); + vrt = new VisualRegressionTracker(vrtConfig()); vrt.start(); } @@ -106,4 +99,13 @@ void vrtTrackWithRetry(String pageName, TestRunOptions testRunOptions, int maxRe vrtTrackWithRetry(pageName, testRunOptions, maxRetries - 1); } + + static VisualRegressionTrackerConfig vrtConfig() { + VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder builder = VisualRegressionTrackerConfig.builder(); + File configFile = new File("vrt.json"); + if (configFile.exists()) { + builder.configFile(configFile); + } + return builder.build(); + } } diff --git a/appium-maven/vrt.json.example b/appium-maven/vrt.json.example new file mode 100644 index 0000000..c372ab5 --- /dev/null +++ b/appium-maven/vrt.json.example @@ -0,0 +1,7 @@ +{ + "apiUrl": "http://localhost:4200", + "apiKey": "DEFAULTUSERAPIKEYTOBECHANGED", + "project": "Default project", + "branchName": "master", + "enableSoftAssert": true +} diff --git a/jdi-light-testng/.gitignore b/jdi-light-testng/.gitignore deleted file mode 100644 index ac399bb..0000000 --- a/jdi-light-testng/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*.iml -.idea/ -target/ -*.zip -*.exe -.logs/ -*.xlsx -.allure/ -allure-results/ -.gradle/ -build diff --git a/jdi-light-testng/pom.xml b/jdi-light-testng/pom.xml deleted file mode 100644 index d1eeb1e..0000000 --- a/jdi-light-testng/pom.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - 4.0.0 - - com.epam.jdi - jdi-light-testng-template - 1.0.0 - Template: JDI Light TestNG - - - - jitpack.io - https://jitpack.io - - - - - chrome - - UTF-8 - 21 - 1.9.26 - https://jdi-testing.github.io/jdi-light/ - 2.35.5 - 2.18.0 - - - - - - com.github.Visual-Regression-Tracker - sdk-java - 5.5.0 - - - - - com.epam.jdi - jdi-light-html - 1.6.3 - - - - - io.qameta.allure - allure-testng - ${allure.testng} - - - org.testng - testng - 7.12.0 - test - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.15.0 - - - org.apache.maven.plugins - maven-surefire-plugin - 3.6.1 - - - ${project.build.directory}/allure-results - - - -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" - - - - - org.aspectj - aspectjweaver - ${aspectj.version} - - - - - io.qameta.allure - allure-maven - ${allure.maven} - - - - - - ${project.basedir}/src/main/resources - - - src/test/resources - true - - - - - \ No newline at end of file diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/TestData.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/TestData.java deleted file mode 100644 index dc9733d..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/TestData.java +++ /dev/null @@ -1,405 +0,0 @@ -package org.mytests.uiobjects.example; - -public class TestData { - public static String TABLE_SNAPSHOOT = "Name Phone Email City" + - " Burke Tucker 076 1971 1687 et.euismod.et@ut.edu GozŽe" + - " Grady Brock (011307) 16843 cursus.et@commodo.org Alcobendas" + - " Harding Lloyd 0800 1111 neque.In.ornare@mauris.co.uk Beauvais" + - " Zachary Hendrix 07624 035724 ipsum.non.arcu@auctorullamcorper.ca Redcliffe" + - " Logan Simmons (01367) 073346 ac.mattis.velit@etmagnisdis.com Gierle" + - " Colby Young 0963 451 3377 In.scelerisque.scelerisque@elitelitfermentum.net Driffield" + - " Peter Mitchell (01256) 43239 ipsum.porta@arcu.edu Duncan" + - " Hamilton Curry (0181) 775 6791 et@dignissimmagnaa.org Chemnitz" + - " Nero Stephens 0845 46 42 pellentesque.Sed@Craseutellus.edu Sachs Harbour" + - " Tanner Bruce 0800 1111 elementum.sem@maurisIntegersem.org Kungälv" + - " Akeem Harrington (0111) 759 1390 ac.arcu.Nunc@enimnislelementum.com Gävle" + - " Benjamin Glenn (016977) 3597 est.mollis@eu.co.uk Chippenham" + - " Zachary Sampson 0800 1111 tempor.erat.neque@consectetueradipiscing.org Kota" + - " Sean Brown 0317 398 0597 nibh.Donec.est@nonquamPellentesque.co.uk Castel San Niccolò" + - " Ferris Mckinney 0800 1111 leo.elementum@sedconsequat.net Langford" + - " Chancellor Cabrera 0383 300 9486 Etiam.vestibulum@Nam.edu Bauchi" + - " Keith Long 0800 364 7432 Integer.mollis.Integer@aliquetlobortisnisi.co.uk The Hague" + - " Dalton Richardson 0395 522 3568 lacus.Quisque.purus@Integeraliquamadipiscing.com Calvera" + - " Clarke Langley 0398 596 6834 porttitor.eros@velitPellentesque.ca Fino Mornasco" + - " Eaton Dyer 055 1441 9535 egestas.lacinia.Sed@Sedeueros.ca Lac-Serent" + - " Tyrone Pena (01329) 37079 nec.ligula@lobortisClassaptent.com Tuscaloosa" + - " Curran Becker 056 1678 9780 nulla@Nunccommodo.org Bruck an der Mur" + - " Damon Bryant 0801 483 9983 est.arcu@est.ca Baton Rouge" + - " Ryan Cline 0845 46 46 euismod.est.arcu@augue.co.uk Cockburn" + - " Judah Wheeler 056 4229 0035 risus.Donec@Quisquefringilla.net Kleinmachnow" + - " Ciaran Dotson (016977) 4778 id@sitamet.edu Rotem" + - " Nash Sharp (016977) 6083 amet@fringillaDonec.org Carovilli" + - " Benedict Bryant 0873 083 9083 non.sapien.molestie@blanditNamnulla.com Gravelbourg" + - " Tarik Hansen (023) 7618 2932 dui.lectus.rutrum@Sedauctor.net Afsnee" + - " Ulric Justice (0115) 392 1246 lacus@Loremipsum.net Matagami" + - " Ray Davis 0800 848169 semper@Phasellusdapibusquam.ca Tongerlo" + - " Beck Smith 07624 185893 nascetur.ridiculus.mus@semvitaealiquam.ca Rancagua" + - " Jelani Stafford 0800 049344 faucibus@nibh.co.uk Leersum" + - " Philip Lopez 0845 46 49 ac@orciadipiscingnon.co.uk Grand Island" + - " Jameson Arnold 0988 427 2322 nisl@blanditviverra.ca Tuscaloosa" + - " Burton Bishop (01025) 570147 semper.dui.lectus@commodoat.org Los Sauces" + - " Preston Monroe 0325 630 0384 neque@sed.com Burnie" + - " Joseph Eaton 0887 569 9679 malesuada@urnaNunc.co.uk La Cruz" + - " Carl Clements 07624 940647 et@mauris.edu Avise" + - " Hoyt Terry (0111) 334 8178 lorem@Phasellusnulla.net Fort St. John" + - " Octavius Espinoza (01629) 05356 Nullam.velit.dui@cursusluctus.edu Logan City" + - " Alexander Villarreal 07624 426932 ornare@neceleifendnon.com Bhuj" + - " Hayden Holmes 0360 901 1201 accumsan.laoreet@utipsumac.ca Weißenfels" + - " Sean Mueller 07638 352622 et.ultrices.posuere@Curabitur.ca Kerkhove" + - " Malcolm Richardson 076 9535 9828 urna.Ut.tincidunt@elitfermentumrisus.co.uk Cleveland" + - " Stewart Spence (023) 6496 7274 et.ultrices.posuere@dolorelit.org Calestano" + - " Quinlan Conrad (016977) 5533 tellus.Aenean@massaIntegervitae.edu Raipur" + - " Conan Blanchard (015246) 18321 dignissim.Maecenas.ornare@tincidunttempus.org Neuruppin" + - " Tate Trujillo 0387 316 9960 mauris.erat@vitaesodales.net Wood Buffalo" + - " Chadwick Bradford (0191) 538 2293 non.quam.Pellentesque@Maurisquis.com Edmundston" + - " Neville Alvarado (010779) 48043 vel.arcu@nuncQuisqueornare.co.uk Bear" + - " Baker Maldonado 0800 380112 sit.amet.luctus@arcuVestibulum.org Pointe-au-Pic" + - " Judah Hardy (011296) 66434 ultrices.posuere.cubilia@rhoncusid.com Orsogna" + - " Keegan Henson 0309 312 4037 eget@tempus.ca Habra" + - " Simon Beard (01306) 069594 In.lorem.Donec@diam.ca Swindon" + - " Hop Santiago 0855 726 9053 tortor.nibh.sit@sodaleselit.net Saint-Mard" + - " Carson Hoover 0320 958 4039 magna.tellus.faucibus@risus.com Patarrá" + - " Jelani Jefferson 07624 127835 metus@Donecsollicitudinadipiscing.co.uk Termes" + - " Dane Clark 076 3329 9156 sit@Integeraliquamadipiscing.ca San Diego" + - " Abraham Bryan 0311 770 4531 velit.in@tellusSuspendisse.ca Chartres" + - " Otto Miller (01009) 642663 orci@ipsumPhasellusvitae.ca Turnhout" + - " Talon Davidson 070 7593 9470 odio.Aliquam@Curabiturdictum.ca Minto" + - " Reed Patel 055 3390 4318 pharetra@Aliquamfringillacursus.co.uk San Cristóbal de la Laguna" + - " Herman Yang 0996 157 4214 elit.pretium@Nulla.edu Berlin" + - " Justin Horton 0800 249 3179 sagittis@lobortisnisinibh.ca Cape Breton Island" + - " Flynn Robertson 056 9705 7346 Lorem@cursus.co.uk Moulins" + - " Baker Lane 055 0842 5343 tincidunt@mi.ca Grosseto" + - " Elliott Neal (0119) 359 1626 non.arcu.Vivamus@cursusdiamat.edu Nueva Imperial" + - " Ray Yang 07624 181309 Nullam.enim@justosit.ca Lacombe County" + - " Ethan Albert (014674) 49440 cursus.luctus.ipsum@Quisquefringillaeuismod.ca Duffel" + - " Daquan Cash 070 0820 0781 Donec@convallisdolor.net Kohima" + - " Marshall Good (0121) 467 5361 Ut.nec.urna@felisadipiscing.ca Morena" + - " Edward Dean 0817 789 5834 eu.metus@Sed.ca West Valley City" + - " Rooney Brooks (0112) 337 7340 ornare.lectus.ante@malesuadamalesuada.com Evansville" + - " Judah Becker (01256) 779536 diam@ut.net Mörfelden-Walldorf" + - " Keaton Hahn 07624 524330 sit.amet@insodaleselit.edu Bollnäs" + - " Craig Mejia 07841 125712 consequat.enim@Vestibulumante.net Paupisi" + - " Kermit Wright 0800 1111 ut.aliquam@Loremipsum.ca Fresno" + - " Vernon Erickson 056 7788 3683 vulputate@quis.edu Cuglieri" + - " Kennedy Wade 0800 1111 auctor.nunc@utdolordapibus.net Radebeul" + - " Sebastian Gibson (0161) 575 6673 odio.auctor.vitae@quis.net Nieuwkerken-Waas" + - " Brennan Klein 056 1403 9853 faucibus@maurisa.edu Exeter" + - " Ryder Morse 0500 343447 nibh@incursuset.edu New Westminster" + - " Ignatius Mullen 0845 46 44 elit.fermentum.risus@utquam.com Tavigny" + - " Malachi Stanton (016969) 67601 interdum.Nunc@Donecporttitortellus.net Koersel" + - " Tanek Sloan 07624 351205 cubilia@laoreetposuere.ca Tiverton" + - " John Meyer 07475 360711 Nunc@duiaugue.net New Westminster" + - " Tarik Hunt (01084) 044437 feugiat@pellentesque.ca Sedgewick" + - " Tate Spears 055 5012 9185 euismod@parturient.com Croydon" + - " Stuart Hopkins 056 1376 7135 pede@semperdui.org Allahabad" + - " Aidan Ray 07624 271603 sem.mollis@anequeNullam.co.uk Valleyview" + - " Griffith Nieves (0121) 592 3029 Vivamus@arcuSed.net Spiere" + - " Jared Kramer 0845 46 45 Proin@mattisvelit.net Haldia" + - " Felix Stevenson (0191) 546 2187 pellentesque.tellus@utpharetrased.co.uk Paisley" + - " Wade Cross 0800 183220 dolor.quam@vehiculaet.com Hugli-Chinsurah" + - " Lee Crane 056 6498 9876 nascetur@risusDonec.org Igboho" + - " Zahir Buchanan (0191) 909 2062 nunc.sed.pede@nec.edu Lac-Serent" + - " Brian Meyer (016977) 0358 mollis.nec@seddictumeleifend.co.uk Houston" + - " Tarik Leach 055 8679 2768 risus@Pellentesqueultricies.net Llaillay" + - " Logan Gamble 0800 1111 ac.arcu.Nunc@fermentum.edu Viggiano" + - " Isabelle Mcdowell (01304) 321760 Nam.nulla.magna@mollisDuissit.ca Kiel" + - " Leila Logan 070 1742 9174 pharetra.sed.hendrerit@lorem.net Fort Laird" + - " Cameran Robertson 0845 46 40 vulputate@estMauriseu.org Laja" + - " Ciara Mcclure 07476 375018 aliquam@eliteratvitae.edu Schagen" + - " Sydnee Williamson (015122) 34852 egestas.a@aliquet.ca Hagen" + - " Fay Cobb (0141) 701 4563 felis.eget@sollicitudincommodo.com Marcedusa" + - " Anika Graham 07624 978050 lacus@augueeutellus.edu Valley East" + - " Callie Mullen 076 6778 6017 egestas@atauctor.co.uk Gallicchio" + - " Illana Fitzgerald 0800 394996 dignissim.Maecenas.ornare@erat.ca Quillón" + - " Kathleen Hughes (0101) 491 2925 odio.Nam.interdum@etnetus.ca Matamata" + - " September Andrews 0827 620 1389 Duis.gravida@orcitinciduntadipiscing.com Sint-Pieters-Leeuw" + - " Martina Davidson (012275) 85154 Donec.vitae.erat@Inmi.co.uk Liverpool" + - " Hedy Phillips 0800 120 9706 risus@netuset.edu Wolfenbüttel" + - " Hilda Hubbard 076 5780 0993 non@lacusNulla.co.uk Henstedt-Ulzburg" + - " Beverly Cameron 07380 723139 ultrices.a.auctor@dignissim.edu Lumaco" + - " Kirsten Drake 07915 777813 tincidunt.tempus@Craspellentesque.org Freiberg" + - " Karleigh Hubbard 0800 593726 Integer.mollis.Integer@Maecenasmalesuadafringilla.net Albiano" + - " Aretha Craft (0131) 721 5375 natoque@id.net Hamme" + - " Kerry Andrews 056 4028 5526 pharetra@eleifendCrassed.net Columbia" + - " Alma Espinoza 0500 215138 montes@Aeneaneuismod.com Giugliano in Campania" + - " Yael Jimenez 070 1654 2995 faucibus@DonecfringillaDonec.com Srinagar" + - " Miriam Holland 070 7849 6859 a@enimgravida.edu Kitscoty" + - " Scarlett Whitley (016552) 76917 cursus.vestibulum@Integeraliquam.net Colombo" + - " Aretha Norris 0845 46 48 Donec@ultriciesligulaNullam.edu Bathurst" + - " Faith Reyes 0349 153 9269 aliquet@consequatauctornunc.org Cunco" + - " Hanna Espinoza (01260) 449000 non.massa.non@cursus.org Ruddervoorde" + - " Yen Gibbs 0800 869684 mattis.Integer@acfeugiat.org Colwood" + - " Regina Obrien 0800 299789 In.lorem.Donec@ligulaAenean.com Osnabrück" + - " Illana Parrish 0800 291 0292 magnis.dis.parturient@elitsedconsequat.co.uk Tumbler Ridge" + - " Sigourney Stein (017821) 17943 rhoncus@purusMaecenas.org Geer" + - " Martha Daugherty 076 4262 4528 mollis.Integer.tincidunt@aliquetdiam.com Slijpe" + - " Piper Figueroa (025) 9423 5980 dis@quispedeSuspendisse.ca Sant'Angelo Limosano" + - " Kylan Schmidt 0886 691 4919 Sed.nunc@nequeNullamnisl.ca Tropea" + - " Zephr Fox 0800 638 9987 a@auguemalesuada.ca Luttre" + - " Clementine Mathews 056 5155 6096 magna.nec.quam@Aliquam.co.uk Sale" + - " Amela Sanders 056 5209 8664 pede@pharetrafeliseget.net Rycroft" + - " Dorothy Sullivan (018212) 78744 sapien.cursus.in@Donecnon.net Kharagpur" + - " Cleo Townsend 070 1931 2864 dolor.dapibus.gravida@Duisvolutpatnunc.net Longchamps" + - " Hope Berry 055 4389 2474 ante.bibendum@Morbiquis.ca La Estrella" + - " Britanney Ferrell 0800 768856 a@dignissimpharetra.co.uk Trollhättan" + - " Kiona Barber (01814) 745705 a@vitaeposuereat.edu Herk-de-Stad" + - " TaShya Sparks 0827 428 9203 dolor.Fusce.mi@aliquetmolestie.net Pali" + - " Wanda Parsons (01931) 351968 Nullam.ut@velitin.ca Rum" + - " Hilda Levy (028) 9688 8144 odio.auctor@accumsaninterdumlibero.co.uk Shawinigan" + - " Camille Little (01934) 41188 erat.eget@magna.co.uk Pointe-du-Lac" + - " Cameran Lamb (013896) 97173 Fusce.aliquet@variusultricesmauris.co.uk Wolfville" + - " Ariana Fowler 0992 295 7191 non@dignissimMaecenas.co.uk San Rafael" + - " Germaine Meyers 055 9407 7364 torquent@ultricies.edu Ponoka" + - " Evelyn Grant (0115) 452 5289 nibh@quislectus.co.uk Hamburg" + - " Alea Shelton (01859) 10515 nulla.Integer.urna@fringillaporttitorvulputate.co.uk Morrinsville" + - " Deanna Best (01925) 749888 ullamcorper.nisl.arcu@Morbi.ca Bosa" + - " Ciara Holland 076 8184 9217 metus.In@Mauris.co.uk Vegreville" + - " Sade Graves 0981 895 3416 quis.tristique@nequeMorbi.edu Hof" + - " Dakota Slater (022) 2809 6060 vel@telluseu.net Gorbea" + - " Cally Conway 056 6442 8976 risus.Donec@massaVestibulumaccumsan.net Kingussie" + - " Ivy Bridges (01390) 362274 cursus.diam.at@Vivamus.org Vitry-sur-Seine" + - " Keiko Vasquez 0800 637578 Vivamus.sit.amet@orci.edu Mesa" + - " Eugenia Mason (01897) 29989 dis@velitAliquamnisl.com Vernon" + - " Pearl Moss (0114) 143 4726 tellus@erosProinultrices.co.uk Cambridge" + - " Xantha Mack 0800 626 7655 felis.Donec@aultriciesadipiscing.ca Edam" + - " Aimee Haley 076 2343 5896 auctor@Mauris.edu Uitkerke" + - " Lucy Stafford 055 7164 6348 et@eumetusIn.net Mayerthorpe" + - " Sierra May 07624 263652 neque.et.nunc@arcu.edu Anchorage" + - " Kalia Hancock (027) 7498 7170 dis@neceuismod.com Mjölby" + - " Cameron Stone 0800 135 8495 pellentesque.eget@dictumeu.ca Castel San Niccolò" + - " Desiree Camacho 0800 1111 In@nisiCumsociis.org Joliet" + - " Jasmine Rosales 056 1299 9850 amet.luctus.vulputate@mus.co.uk Morgex" + - " Maggie Atkinson 07085 619068 magna.nec@nislelementumpurus.ca Gondiya" + - " Sydnee Vang 0894 081 3339 nunc.risus@ultrices.ca Canterano" + - " Alika Garcia (0110) 164 3404 sollicitudin.orci.sem@tellus.edu Solingen" + - " Audrey Kaufman 0804 296 0086 Sed.eget@eros.com Miranda" + - " Riley Valenzuela 0500 723497 accumsan@augueac.org Châlons-en-Champagne" + - " Adrienne Mathews 0800 1111 magna@aliquetmetusurna.com Jumet" + - " Hadassah Lynch 055 6189 0259 neque.venenatis.lacus@gravidaPraesenteu.com Hamilton" + - " Allegra Dixon 0845 46 41 imperdiet.ornare.In@quispedePraesent.org Presles" + - " Gretchen Ball 0800 166 6580 tortor.dictum@malesuadaiderat.com Guntur" + - " Mercedes Garza 056 7478 1990 euismod.mauris@Cras.edu Francavilla Fontana" + - " Kathleen Romero 07388 486341 Cras.vulputate.velit@famesac.net Isola del Gran Sasso d'Italia" + - " Joelle Burton 0500 340113 urna.Ut@Inatpede.ca Jackson" + - " Tashya Barrera (0141) 909 6538 tristique@nuncrisus.edu Dubbo" + - " Nicole Gregory (01010) 02244 aliquet.Phasellus@ante.edu Emden" + - " Minerva Parsons 0971 347 7087 odio@etnetuset.net Rosenheim" + - " Shelley Hale 056 3940 1855 egestas.Duis.ac@magnaPraesent.co.uk Kortrijk" + - " Lael Osborn 07701 189978 non@interdumligula.org Lübeck" + - " Rinah Ross 070 3885 8017 Mauris@malesuadavel.co.uk Mühlheim am Main" + - " Isabelle Erickson 0372 669 1072 sollicitudin@Donecluctusaliquet.net Lincoln" + - " Halee Bernard (016977) 0233 nec@duiSuspendisse.org Mal" + - " Pandora Bell 07624 568917 semper.tellus@eratvitae.co.uk Kalgoorlie-Boulder" + - " Chelsea Vargas (01643) 33412 ante@VivamusnisiMauris.com Imst" + - " Shafira Nixon 07624 087175 in@euismod.ca Driekapellen" + - " Bertha Nelson 0800 415119 In@Curabitursedtortor.net Rio Verde" + - " Helen Carter 07916 049237 hendrerit.id.ante@Duisgravida.org Puerto Montt" + - " Madeline Good (017187) 85467 massa.Suspendisse.eleifend@non.co.uk Terni" + - " Sydney Hughes 076 6334 5393 nunc.ullamcorper@neque.ca Barahanagar" + - " Fiona Noel (016977) 7074 est.Mauris.eu@quamdignissim.co.uk Waiheke Island" + - " Maisie Graham 0800 139993 Sed.id@necante.co.uk Dijon" + - " Germaine Brooks 0302 964 6397 ullamcorper@temporlorem.co.uk Ranst" + - " Gwendolyn Stephenson (0121) 508 7000 libero.Proin@tristique.co.uk Lasne-Chapelle-Saint-Lambert" + - " Minerva Justice 0500 218264 risus@Suspendissenon.edu North Cowichan" + - " Joan Tyler (0191) 405 8266 elit.Curabitur@facilisismagna.ca Sittard" + - " Phyllis Mathews (01622) 238340 ac@Donecfeugiatmetus.com Castelbuono" + - " Lacey Ortega 0370 539 3423 hendrerit.neque.In@lorem.net Jolanda di Savoia" + - " Jeanette Berg 0845 46 45 enim.diam@tincidunt.ca San Martino in Pensilis" + - " Brynne Holt 07304 543587 id.enim@temporeratneque.com Philadelphia" + - " Xandra Little 055 8256 4039 Etiam@parturientmontes.org Bad Nauheim" + - " Quemby Waters 07624 552770 eu.tellus@penatibus.ca Bonnyville" + - " Jessica Richardson (016977) 1368 dictum.magna.Ut@acarcu.edu Williams Lake" + - " Athena Greene 07624 582787 adipiscing.non@etrutrum.org Calera" + - " Alika Palmer (0101) 285 8741 orci.luctus.et@morbitristiquesenectus.co.uk Curitiba" + - " Callie Wall 055 4572 5553 Morbi.metus.Vivamus@Donec.co.uk Turrialba" + - " Ocean Knapp 0800 475 1597 natoque@augueidante.ca Les Bons Villers" + - " Cailin Vance (0110) 915 5385 Lorem.ipsum.dolor@magnis.org Heist-aan-Zee" + - " Delilah Conner 070 5760 4445 Curae.Phasellus.ornare@loremfringilla.com Lossiemouth" + - " Yuri Cochran 0800 1111 sed.dolor.Fusce@vitaediam.org Rockingham" + - " Destiny Hamilton 056 4319 5840 quis@Nullatempor.co.uk Baranello" + - " Ava Nolan 0321 471 2222 quis@ridiculusmus.net Torgnon" + - " Hilary Willis 070 8125 5491 faucibus@nulla.net Sangli" + - " Jenna Owens 07917 564744 nec.tellus@Fusce.co.uk Düsseldorf" + - " Carly Marsh 07015 676248 sagittis@hendreritid.org Parndorf" + - " Quintessa Kirby 070 3296 1629 tristique@et.ca Springfield" + - " Montana Hull (016977) 3710 vel.mauris.Integer@non.org Livo" + - " Catherine Reyes (01089) 452406 molestie@etarcu.com Fontanellato" + - " Summer Rice (01187) 486652 justo@et.org Radom" + - " Christine Mccoy 0800 152 5114 vel.arcu.eu@lobortis.net Rochester" + - " Alexa May 070 1215 1314 ultricies.dignissim@dolorsitamet.edu Pointe-Claire" + - " Myra Rodgers 070 4681 9771 sit@ut.net Drayton Valley" + - " Desirae Matthews (0101) 628 7979 Nulla@egetlaoreet.ca Dieppe" + - " Francesca Knowles (01693) 853716 volutpat.Nulla@magnaetipsum.co.uk Broken Arrow" + - " Quemby Rich 0845 46 42 vestibulum.nec@eunulla.edu Red Deer" + - " Mara Harrison 056 4589 0890 ut.eros.non@maurisIntegersem.org Baton Rouge" + - " Kitra Solis (0114) 741 5753 Quisque@Quisquefringillaeuismod.co.uk Broken Arrow" + - " Sandra Larson 07098 082333 fermentum@vitaenibh.net Nîmes" + - " Iola Cash 07624 092172 tellus.lorem.eu@vehiculaPellentesque.edu Caprino Bergamasco" + - " Kitra Sanchez 0800 674733 Phasellus@ipsumCurabiturconsequat.ca Wollongong" + - " Emily Keith 055 2769 6445 arcu@consectetuerrhoncusNullam.com Kent" + - " Kessie Schroeder 0967 691 5385 auctor.Mauris.vel@acarcuNunc.org Glabais" + - " Kaitlin Fernandez (020) 6003 1394 Donec@erat.edu Parbhani" + - " Doris Vaughn (0118) 591 3483 tincidunt.vehicula.risus@Proinegetodio.ca Sotteville-lès-Rouen" + - " Glenna Witt (017562) 27891 dis@Inmipede.org Soye" + - " Emma Howe (0117) 274 2390 luctus@dictum.net Zonhoven" + - " Blair Banks 070 5106 5627 metus.Vivamus.euismod@sagittis.edu Kingston-on-Thames" + - " Jorden Durham 07624 691948 molestie.in.tempus@tristiquepharetra.com Nampa" + - " Blythe Underwood (028) 0051 0929 iaculis.aliquet@sed.edu St. Thomas" + - " Eleanor Mosley 055 9116 9366 Vivamus.non.lorem@mauris.edu Tresigallo" + - " Clementine West 076 5845 0260 ac.turpis.egestas@Nuncmauris.com Acciano" + - " Eleanor Hudson (013649) 59185 Sed.eget@blanditcongueIn.net Orsara di Puglia" + - " Indigo Shields 0800 744604 ipsum@pharetra.co.uk Dorgali" + - " Galena Hughes 070 5628 7648 ut.erat@neccursus.ca Kampenhout" + - " Cassidy Travis 0845 46 40 est.Mauris@Aliquam.net Gateshead" + - " Virginia Mason 0845 46 46 lorem@nondapibus.ca Istres" + - " Cecilia Ferguson 070 0771 0692 erat.Vivamus.nisi@natoquepenatibuset.ca Böblingen" + - " Eleanor Harris (0114) 633 1360 Proin.nisl.sem@Curabituregestasnunc.com Bazel" + - " Illana Shepherd (01385) 30910 aliquam.eros@adipiscingenim.ca Beerst" + - " Kaitlin Sharpe 0849 458 7485 non@lacinia.com Flin Flon" + - " Beatrice Day 07624 875300 netus.et@arcuvel.com Fort Worth" + - " Naomi Cunningham 055 5974 6779 Morbi.neque@Vestibulumanteipsum.edu Palermo" + - " Ruth Moses (0114) 392 6720 erat@Aliquam.edu Birmingham" + - " Jaime Hopper 07624 169480 libero.est@magnaSedeu.com Aisemont" + - " Bertha Mann 0963 581 1773 placerat@tristiquepharetraQuisque.edu Nerem" + - " Melissa Gomez 056 7293 3021 parturient.montes@In.com Soye" + - " Ainsley Parks 0866 858 0276 mi.fringilla.mi@ut.com Fratta Todina" + - " Virginia Lang 0845 46 44 est.Mauris@enimNuncut.edu Chantemelle" + - " Ila Harper 0845 46 48 molestie.dapibus@seddictum.edu Les Bons Villers" + - " Lee Wilder 07624 189677 sapien.Aenean.massa@etcommodoat.ca Lakeland County" + - " Stacy Webster 0800 1111 Fusce.dolor.quam@velconvallisin.edu Antuco" + - " Isadora Joseph (027) 3824 1305 vulputate.eu@felisNullatempor.org Dorval" + - " Alea Guzman (01571) 95927 Donec@erat.net Pickering" + - " Leandra Shepherd 07624 116495 Nam.nulla@leoin.com Reading" + - " Aiko Donaldson 0303 541 3532 ligula@Innecorci.co.uk Wick" + - " Cathleen Mcdowell (016977) 4272 In.nec.orci@molestiepharetranibh.edu Rossignol" + - " Indigo Cantrell 076 4382 9948 Maecenas.malesuada.fringilla@dolor.org Sint-Martens-Bodegem" + - " Beverly Reilly (0151) 098 3418 pede@erategettincidunt.net Fraserburgh" + - " Leandra Decker 070 3077 7528 fringilla.est.Mauris@neceleifendnon.net Bowling Green" + - " Angela Woodard 07624 305700 fermentum.arcu@nonnisi.ca Zerkegem" + - " Savannah Castro (016977) 8937 Etiam@ligula.org Carleton" + - " Hollee Stark 0800 989 0403 gravida@sociosqu.edu Porretta Terme" + - " Demetria Gonzales 0800 122836 rutrum@dictum.ca Coquitlam" + - " Ina Rivers 0826 825 8806 neque@lobortisClassaptent.com Melilla" + - " Imelda Wilkerson 0800 1111 feugiat.nec@Duisdignissim.org Ribnitz-Damgarten" + - " Mariko Watson (01820) 91473 nec.luctus@facilisisfacilisis.org Fontaine-Valmont" + - " Alfreda Fox (0161) 505 2769 quis.massa@arcuSedet.edu Tongerlo" + - " Laura Boyer 0500 886398 tellus.Nunc@Curabitur.ca Bargagli" + - " Cara Mcleod 0800 950 6544 Suspendisse.eleifend.Cras@scelerisquenequesed.ca Imst" + - " Odette Baird 0800 067654 turpis.nec@pedeNunc.com Monstreux" + - " Hilda Cummings 076 3929 0120 egestas@acnullaIn.com Galzignano Terme" + - " Nerea Wade (016977) 8450 Maecenas.libero@lobortisultrices.com Chelsea" + - " Idola Workman (0114) 121 3175 egestas.a.dui@NullafacilisisSuspendisse.ca Folx-les-Caves" + - " Sonia Hahn (01111) 317928 ut@ultrices.com Altach" + - " Moana Clements (0113) 521 1388 sed@quis.ca Portsmouth" + - " Brittany Lester 056 0424 3586 Integer.vulputate@et.ca Linton" + - " Jenna Espinoza 0500 855288 Maecenas@feugiatplaceratvelit.net Cerchio" + - " Charde Powell 0395 732 6493 Aenean@Proin.net Paradise" + - " Iona Walton (0111) 925 9148 adipiscing.elit@ametdapibus.edu San Cesario di Lecce" + - " Cameron Wilcox 07196 574529 velit.dui@elitpharetraut.net Titagarh" + - " Gwendolyn Walters 0343 081 8534 lobortis@quisdiam.edu Cape Breton Island" + - " Kameko Hewitt (015192) 50969 velit.in@ultricesposuerecubilia.com Angleur" + - " Alice Joyner 055 8960 3696 dui.nec@Uttinciduntorci.co.uk Ponte San Nicolò" + - " Imani Santana 07624 358679 Cum.sociis@velitPellentesque.edu Nelson" + - " Carol Albert (029) 0001 3450 eu.sem@atfringilla.org Opheylissem" + - " Amethyst Ellison (024) 5122 9525 dictum@nibhPhasellus.co.uk Hulste" + - " Sheila Walton 0800 456077 pellentesque@egetvarius.net Harrisburg" + - " Indira Garrison 056 4760 2993 posuere.at@perconubia.edu Cisnes" + - " Tara Bradshaw (011429) 24082 erat.in.consectetuer@Sed.co.uk Embourg" + - " Kevyn Stevenson 07624 702264 eu@lacusQuisque.edu Mainz" + - " Ariel Nielsen (020) 2609 8789 eget.mollis@ridiculusmus.com Paine" + - " Chelsea Clark 076 8209 9598 parturient.montes.nascetur@ligulaDonecluctus.org North Bay" + - " Sonia Buckley (0117) 339 8426 amet@fermentum.ca Fraser-Fort George" + - " Paula Davidson 0800 1111 fringilla.purus@Aeneanegetmetus.co.uk Petacciato" + - " Sylvia Oneil 0500 952754 Phasellus.dapibus@CrasinterdumNunc.net Chandannagar" + - " Giselle Fleming (0110) 075 9118 eu@elita.com Dietzenbach" + - " Aimee Moody (01582) 01208 at.egestas.a@tristique.net Irricana" + - " Gail Rosario (0110) 640 2306 pede.Suspendisse@volutpatNulla.com Namen" + - " Whoopi Sharpe 0863 296 7004 diam@Suspendissetristiqueneque.ca Vedrin" + - " Judith Mack 070 7687 7888 Donec.nibh@rutrumjusto.ca Ternitz" + - " Helen Mckee (023) 3562 7053 vehicula.Pellentesque.tincidunt@temporeratneque.co.uk Canmore" + - " Kirestin Ballard (01343) 604851 Donec.consectetuer@ProindolorNulla.ca Nagpur" + - " Brianna Pitts 070 8440 6981 eu@temporarcuVestibulum.net Chiniot" + - " Daryl Baker 0500 000711 Morbi.non@Quisque.com Seilles" + - " Carla Bryant 070 4282 2093 Mauris.magna@vehiculaaliquetlibero.co.uk Freital" + - " Rina Nixon 076 0303 9562 Cras.vulputate.velit@utcursus.ca Tiel" + - " Ginger Jennings 070 5712 6754 leo@ligula.co.uk Burnpur" + - " Cecilia Yates (0110) 316 7904 sollicitudin.adipiscing.ligula@aliquam.co.uk Asse" + - " Belle Meyer 0800 249 5308 Donec.egestas@blanditcongue.org İmamoğlu" + - " Rebekah Farley (01394) 289332 eget.lacus@convallisante.co.uk Morrinsville" + - " Remedios Hernandez 07624 301755 Donec.est@Fusce.edu Guadalajara" + - " Breanna Beck 076 3963 3445 nunc.sit@enim.com Saint-Prime" + - " Aurelia Miller 070 9147 4394 lorem.ac@ametorciUt.co.uk Matamata" + - " Mara Yang (0118) 405 4779 dui.nec.urna@Namnulla.org Wolfenbüttel" + - " Candace Roberson 0500 874367 non.justo@lacus.net Stony Plain" + - " Adara Le 056 2666 4455 cubilia.Curae.Phasellus@atpretium.edu Sant'Egidio alla Vibrata" + - " Aspen Lawson (021) 3113 7798 consequat.purus@ipsumdolorsit.edu Lexington" + - " Ursula Burgess 07624 179190 ac.eleifend.vitae@orciin.org Rathenow" + - " Dara Meyers (025) 0943 3346 dolor@justo.com Traralgon" + - " Galena Joyce (01080) 558650 id@nonummy.org Huntsville" + - " Jada Reese (0117) 739 3237 nec.mauris.blandit@auctorodioa.ca Conca Casale" + - " Amy Maynard 076 8426 2119 sit@Aliquam.net Geel" + - " Sydney Farmer 055 4135 3834 natoque.penatibus.et@etnetus.edu Pinneberg" + - " Dai Drake (025) 3091 8842 Cras.lorem@Maurisnondui.org Exeter" + - " Janna Sanchez 0800 087282 erat.eget.ipsum@Nullam.org Sart-Eustache" + - " Maite Schwartz 056 8184 8460 pharetra.sed.hendrerit@vitaedolor.org Longvilly" + - " Amity Coffey 0937 570 2428 laoreet.lectus@dolorsitamet.ca Llangefni" + - " Willa Snyder (020) 4175 7353 ullamcorper.eu.euismod@placerat.net Pavone del Mella" + - " Regan Patton 0800 086128 elit.Curabitur.sed@Etiamvestibulum.com Paradise" + - " Karly Keller (018284) 65790 facilisis.vitae@dictumProin.org Orp-Jauche" + - " Ivy Dudley 070 8060 2941 Donec.non@Etiamvestibulummassa.net Borås" + - " Hope Hoffman 0845 46 43 vulputate.lacus.Cras@hendreritaarcu.ca Township of Minden Hills" + - " Alma Cleveland (0112) 149 7083 ante.dictum.cursus@ad.org Warri" + - " Hillary Calhoun (0119) 984 5378 justo.sit.amet@molestiepharetranibh.co.uk Hampstead" + - " Blossom Kim (016977) 3756 pellentesque.eget@egestas.edu Belsele" + - " Judith Graves 0851 556 3717 Curabitur@dolor.ca Vanderhoof" + - " Xerxes Lyons 070 7165 5290 nunc@magnaDuis.co.uk Aurangabad" + - " Dominique Parrish 0800 535186 nonummy.Fusce@ultricesDuisvolutpat.net Scarborough" + - " Yael Weeks (01813) 26044 nulla@Crasvulputate.com Fairbanks" + - " Nicole Summers (01545) 082490 magna@ametdiameu.edu Itapipoca" + - " Jana Young 07996 242955 tristique@iaculisneceleifend.net Tiegem" + - " Hollee Pruitt 0800 313 9810 faucibus.leo@Seddictum.org Bhopal" + - " Kylie Gates 056 2716 8367 luctus.aliquet.odio@utipsum.com San Massimo" + - " Ramona Cochran 07722 213674 purus.accumsan@luctuslobortisClass.com Cerro Navia" + - " Charissa Rosario 070 4770 6703 amet.orci@nonenimcommodo.co.uk Innisfail" + - " Madaline Blanchard (027) 5118 9913 eleifend.nunc@uteros.org Lamont" + - " Jemima Ayers 076 0902 8977 urna.Ut.tincidunt@dis.com Sonnino" + - " Quinn England 0800 080 0584 et.lacinia@torquent.net Brampton" + - " Susan Simon 0800 853 9239 magnis.dis.parturient@milorem.org Marbais" + - " Veda Moreno 055 8837 4859 ligula@inhendrerit.net Madrid" + - " Brielle Buckner 0800 1111 egestas.a.dui@dictumcursus.org Fürstenwalde" + - " Dorothy Brewer (012409) 17477 consequat.purus.Maecenas@convalliserat.net Mansfield-et-Pontefract" + - " Roanna Frank 0500 534552 cursus.vestibulum@atrisusNunc.com Serramonacesca" + - " Quin Barron (01902) 16701 lacus.Nulla.tincidunt@imperdietnon.net Heusden-Zolder" + - " Geraldine Nash (016977) 5728 dui.augue.eu@aodiosemper.co.uk Pessac" + - " Hadley Lambert (0114) 565 6236 sed.facilisis.vitae@Integer.co.uk Jandrain-Jandrenouille" + - " Chelsea Hubbard 056 2585 7846 in.sodales.elit@tempuseuligula.ca Zaltbommel" + - " Yetta Clemons (016977) 1475 et.magnis.dis@quamelementumat.co.uk Knokke" + - " Jenna Humphrey 056 5627 7953 Cras.dictum@dignissimpharetra.edu Maple Creek" + - " Genevieve Rollins (0101) 162 9782 sed.hendrerit@Cumsociis.org Mazzano Romano" + - " Miranda Moon (021) 7795 7957 sagittis.semper@tristiquepellentesque.net Saint John" + - " Willow Castillo 070 7358 9294 felis.Nulla.tempor@musDonecdignissim.com Montalto Uffugo" + - " Anastasia Mueller (016977) 1314 a@lacuspedesagittis.com Morrinsville" + - " Dara Owens 076 6868 7201 tellus.Phasellus@nuncsed.co.uk Waterbury" + - " Hanae Rice 0912 095 9668 nunc@aliquetliberoInteger.co.uk Rum" + - " Rama Wooten 076 0401 2139 feugiat.tellus@estmaurisrhoncus.co.uk Baidyabati" + - " Gisela Solis 070 3439 4629 non@auctorvelit.com Chambord" + - " Jana Robinson 07013 834562 libero.Proin@tinciduntnibhPhasellus.org Roccanova" + - " Claire West 0800 1111 orci.Ut.sagittis@quamelementum.co.uk Ligosullo" + - " Leigh Baldwin 0327 753 1329 quis@euismodenimEtiam.com Teralfene" + - " Chastity Mullen (0112) 945 1682 felis.eget.varius@lacinia.ca Mariquina" + - " Cara Mooney (0113) 735 6895 felis.purus@fermentumvelmauris.com Palagano" + - " Zenia Mcmillan (015652) 02443 eu.turpis.Nulla@nascetur.ca Civitacampomarano" + - " Portia Hill (01983) 17408 a.arcu.Sed@arcuvelquam.org Colobraro" + - " Brynne Brewer 0800 832 1988 amet@vitaerisus.edu Athens" + - " Kiayada Hendricks 07779 546753 interdum@dignissimtemporarcu.co.uk Lachine" + - " Lillith Reese 07624 007787 dolor.tempus@ultriciesdignissim.com Freux" + - " Keelie Gallagher (01586) 42913 dapibus.ligula@sit.net Campbelltown" + - " Ariana Keith 0845 46 40 ipsum.dolor.sit@auctorveliteget.com Karimnagar" + - " Jessica Koch 0845 46 42 nec.ante@Praesenteunulla.ca Osgoode" + - " Aileen Rodriguez 0845 46 46 mattis.velit.justo@Maurismolestie.com Portico e San Benedetto" + - " Germane Brewer 076 5624 2680 malesuada.ut.sem@Lorem.net El Bosque" + - " Willow Park 07196 875331 Vivamus.rhoncus.Donec@ProinvelitSed.org Bever Bievene" + - " Yen Stevenson 07624 310007 in@Sedeu.edu Imphal" + - " Ivana Morse (01207) 692187 eu.metus@quis.edu Pickering" + - " Chanda Donaldson 055 5125 3654 sit.amet.luctus@vellectus.ca Vidisha"; -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/custom/Avatar.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/custom/Avatar.java deleted file mode 100644 index b7cde45..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/custom/Avatar.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.mytests.uiobjects.example.custom; - -import com.epam.jdi.light.elements.composite.Section; -import com.epam.jdi.light.elements.interfaces.base.HasValue; -import com.epam.jdi.light.elements.pageobjects.annotations.locators.UI; -import com.epam.jdi.light.ui.html.elements.common.Checkbox; -import com.epam.jdi.light.ui.html.elements.common.Image; -import com.epam.jdi.light.ui.html.elements.common.Text; - -public class Avatar extends Section implements HasValue { - @UI("img") public Image image; - @UI(".user-descr>span")public Text description; - @UI("input[type=checkbox]")public Checkbox vip; - - @Override - public String getValue() { - return description.getText() + ":" + - (vip.isSelected() ? "VIP" : "Dude"); - } -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/custom/MarvelUser.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/custom/MarvelUser.java deleted file mode 100644 index b442caa..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/custom/MarvelUser.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.mytests.uiobjects.example.custom; - -import com.epam.jdi.light.elements.complex.dropdown.Dropdown; -import com.epam.jdi.light.elements.composite.Section; -import com.epam.jdi.light.ui.html.elements.common.Link; -import com.epam.jdi.light.ui.html.elements.common.Text; - -public class MarvelUser extends Section { - public Text number; - public Dropdown type; - public Link user; - public Avatar description; -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/Contacts.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/Contacts.java deleted file mode 100644 index d5da1eb..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/Contacts.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.mytests.uiobjects.example.entities; - -import com.jdiai.tools.DataClass; - -public class Contacts extends DataClass { - public String firstName, lastName, acceptConditions, position, passportNumber, - passportSeria, description, gender, weather; - public boolean passport; -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/Defaults.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/Defaults.java deleted file mode 100644 index bba6b1a..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/Defaults.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.mytests.uiobjects.example.entities; - - -public class Defaults { - public static User DEFAULT_USER = new User(); - - public static Contacts DEFAULT_CONTACT = new Contacts().set(c-> { - c.firstName = "Roman"; c.lastName = "Iovlev"; c.position = "ChiefQA"; - c.passport = true; c.passportNumber = "4321"; c.passportSeria = "123456"; - c.description = "JDI - awesome UI automation tool"; c.acceptConditions = "true"; - c.gender = "Female"; c.weather = "Sun, Rain"; - }); -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/LeftMenuData.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/LeftMenuData.java deleted file mode 100644 index 09af714..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/LeftMenuData.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.mytests.uiobjects.example.entities; - -/** - * Created by Roman_Iovlev on 3/2/2018. - */ -public enum LeftMenuData { - Home("Home"), - ContactForm("Contact form"), - Support("Support"), - Dates("Dates"), - Service("Service"), - ComplexTable("Complex Table"), - SimpleTable("Simple Table"), - UserTable("User Table"), - TableWithPages("Table with pages"), - DifferentElements("Different elements"), - MetalsColors("Metals & Colors"), - Performance("Performance"), - ElementsPacks("Elements packs"), - HTML5("HTML 5"), - Bootstrap("Bootstrap"); - - public String value; - LeftMenuData(String value) { this.value = value; } -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/MarvelUserInfo.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/MarvelUserInfo.java deleted file mode 100644 index fa5ef43..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/MarvelUserInfo.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.mytests.uiobjects.example.entities; - -import com.jdiai.tools.DataClass; - -public class MarvelUserInfo extends DataClass { - public String number, type, user, description; -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/User.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/User.java deleted file mode 100644 index 901f991..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/entities/User.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.mytests.uiobjects.example.entities; - - -import com.jdiai.tools.DataClass; - -public class User extends DataClass { - public String name = "Roman"; - public String password = "Jdi1234"; -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/SiteJdi.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/SiteJdi.java deleted file mode 100644 index 8f84384..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/SiteJdi.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.mytests.uiobjects.example.site; - -import com.epam.jdi.light.elements.common.UIElement; -import com.epam.jdi.light.elements.complex.JList; -import com.epam.jdi.light.elements.complex.Menu; -import com.epam.jdi.light.elements.composite.Form; -import com.epam.jdi.light.elements.pageobjects.annotations.locators.Css; -import com.epam.jdi.light.elements.pageobjects.annotations.locators.UI; -import org.mytests.uiobjects.example.entities.User; -import org.mytests.uiobjects.example.site.custom.MenuItem; -import org.mytests.uiobjects.example.site.pages.*; -import org.openqa.selenium.WebElement; - -//@JSite("https://jdi-testing.github.io/jdi-light/") -public class SiteJdi { - public static HomePage homePage; - public static JDIPerformancePage performancePage; - public static ContactFormPage contactFormPage; - public static ContactsPage contactsPage; - public static DatesPage datesPage; - public static Html5Page html5Page; - public static UsersPage usersPage; - @Css("form") public static Form loginForm; - - @Css(".profile-photo [ui=label]") public static UIElement userName; - @Css(".logout") public static WebElement logout; - @Css("img#user-icon") public static UIElement userIcon; - @UI(".sidebar-menu li") public static Menu leftMenu; - @UI(".sidebar-menu li") public static JList menu; - -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MenuItem.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MenuItem.java deleted file mode 100644 index 4bbf83a..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MenuItem.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.mytests.uiobjects.example.site.custom; - - -import com.epam.jdi.light.elements.common.UIElement; - -public class MenuItem extends UIElement { - @Override - public boolean isSelected() { - return hasClass("active") && attr("ui").equals("label"); - } -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MultiDropdown.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MultiDropdown.java deleted file mode 100644 index fd61e18..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MultiDropdown.java +++ /dev/null @@ -1,252 +0,0 @@ -package org.mytests.uiobjects.example.site.custom; - -import com.epam.jdi.light.asserts.generic.UISelectAssert; -import com.epam.jdi.light.common.JDIAction; -import com.epam.jdi.light.common.TextTypes; -import com.epam.jdi.light.elements.base.JDIBase; -import com.epam.jdi.light.elements.base.UIListBase; -import com.epam.jdi.light.elements.common.UIElement; -import com.epam.jdi.light.elements.interfaces.base.HasLabel; -import com.epam.jdi.light.elements.interfaces.base.ICoreElement; -import org.openqa.selenium.By; - -import java.util.List; - -import static com.epam.jdi.light.driver.WebDriverByUtils.fillByTemplate; -import static com.epam.jdi.light.elements.init.UIFactory.$; -import static com.epam.jdi.light.elements.init.UIFactory.$$; -import static com.epam.jdi.light.logger.LogLevels.DEBUG; -import static com.jdiai.tools.EnumUtils.getEnumValues; -import static com.jdiai.tools.LinqUtils.*; -import static java.util.Arrays.asList; -import static org.apache.commons.lang3.StringUtils.isBlank; - -public class MultiDropdown extends UIListBase> - implements ICoreElement, HasLabel { - - By expandArrow = By.cssSelector(".caret"); - By values = By.tagName("li"); - By valueTemplate = By.xpath(".//a[label[text()='%s']]"); - By value = By.cssSelector("button"); - By valuesConatiner = By.tagName("ul"); - - UIElement root() { return $(By.xpath(".."),this).setName("root"); } - UIElement expander() { return root().find(expandArrow).setName("expandArrow"); } - UIElement valuesList() { return root().find(valuesConatiner).setName("valuesContainer"); } - UIElement value(String name) { - return root().find(fillByTemplate(valueTemplate, name)).setName("valueTemplate"); - } - UIElement valueText() { return root().find(value).setName("value"); } - List allValues() { - return root().finds(values).indexFromZero(); - } - - @JDIAction(level = DEBUG) - private void expand() { - if (valuesList().setup(JDIBase::noValidation).isHidden()) - expander().click(); - } - - /** - * Selects values from parameters - * @param names String var arg, elements with text to select - */ - @JDIAction("Select '{0}' for '{name}'") - public void select(String... names) { - expand(); - for (String name : names) { - UIElement value = value(name); - if (value.isEnabled()) - value.click(); - } - } - - /** - * Selects only particular elements - * @param indexes String var arg, elements with text to select - */ - @JDIAction("Select '{0}' for '{name}'") - public void select(int... indexes) { - expand(); - for (int i = 1; i <= indexes.length; i++) { - UIElement value = $$(values, this).get(indexes[i]); - if (value.isEnabled()) - value.click(); - } - } - - public void check(String names) { - if (isBlank(names)) return; - check(names.split(",")); - } - - /** - * Selects only particular elements - * @param names String var arg, elements with text to select - */ - @JDIAction("Check '{0}' for '{name}'") - public void check(String... names) { - expand(); - List listNames = map(names, String::trim); - for (String name : values()) { - UIElement value = value(name); - boolean valueSelected = value.find("input").setup(JDIBase::noValidation) - .isSelected(); - if (valueSelected && !listNames.contains(name.trim()) - || !valueSelected && listNames.contains(name.trim())) - value.click(); - } - label().click(); - } - - /** - * Unselects only particular elements - * @param names String var arg, elements with text to unselect - */ - @JDIAction("Uncheck '{0}' for '{name}'") - public void uncheck(String... names) { - expand(); - List listNames = asList(names); - for (String name : values()) { - UIElement value = value(name); - if (value.isDisabled()) continue; - if (value.isSelected() && listNames.contains(value.getText().trim()) - || !value.isSelected() && !listNames.contains(value.getText().trim())) - value.click(); - } - } - public > void check(TEnum... values) { - check(getEnumValues(values)); - } - - public > void uncheck(TEnum... values) { - uncheck(getEnumValues(values)); - } - - /** - * Checks particular elements by index - * @param indexes int var arg, ids to check - */ - @JDIAction("Check '{0}' for '{name}'") - public void check(int... indexes) { - expand(); - List listIndexes = toList(indexes); - for (int i = 1; i <= values().size(); i++) { - UIElement value = allValues().get(i-1); - if (value.isDisabled()) continue; - if (value.isSelected() && !listIndexes.contains(i) - || !value.isSelected() && listIndexes.contains(i)) - value.click(); - } - } - - /** - * Unchecks particular elements by index - * @param indexes int var arg, ids to uncheck - */ - @JDIAction("Uncheck '{0}' for '{name}'") - public void uncheck(int... indexes) { - expand(); - List listIndexes = toList(indexes); - for (int i = 1; i <= values().size(); i++) { - UIElement value = allValues().get(i-1); - if (value.isDisabled()) continue; - if (value.isSelected() && listIndexes.contains(i) - || !value.isSelected() && !listIndexes.contains(i)) - value.click(); - } - } - - /** - * Gets checked values in dropdown - * @return List - */ - @JDIAction("Get '{name}' checked values") - public List checked() { - return ifSelect(allValues(), UIElement::isSelected, UIElement::getText); - } - - /** - * Selects value in dropdown - * @param value String var arg - */ - @JDIAction("Select '{0}' for '{name}'") - public void select(String value) { - select(new String[]{value}); - } - - /** - * Selects value with index in dropdown - * @param index int var arg - */ - @JDIAction("Select '{0}' for '{name}'") - public void select(int index) { - select(new int[]{index}); - } - - /** - * Gets a list of text from each values from dropdown - * @return List - */ - @JDIAction("Get '{name}' list values") - public List values() { - return map(allValues(), UIElement::getText); - } - - /** - * Gets a list of innerText from each values from dropdown - * @return List - */ - @JDIAction("Get '{name}' values") - public List values(TextTypes type) { - return map(allValues(), o -> o.text(type)); - } - - /** - * Gets enabled values from dropdown - * @return List - */ - @JDIAction("Get '{name}' enabled values") - public List listEnabled() { - return ifSelect(allValues(), UIElement::isEnabled, UIElement::getText); - } - - /** - * Gets disabled values from dropdown - * @return List - */ - @JDIAction("Get '{name}' disabled values") - public List listDisabled() { - return ifSelect(allValues(), UIElement::isDisabled, UIElement::getText); - } - - @Override - public void setValue(String value) { - check(value); - } - - /** - * Gets selected value - * @return String - */ - @Override - @JDIAction("Get '{name}' selected value") - public String selected() { - return valueText().getText(); - } - - /** - * Checks if a value is selected in a dropdown - * @param value String to select - * @return boolean - */ - @JDIAction("Is '{0}' selected for '{name}'") - public boolean selected(String value) { - return selected().trim().equalsIgnoreCase(value.trim()); - } - - @Override - public String getValue() { - return selected(); - } -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MyCheckList.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MyCheckList.java deleted file mode 100644 index be6109d..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/custom/MyCheckList.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.mytests.uiobjects.example.site.custom; - -import com.epam.jdi.light.elements.common.UIElement; -import com.epam.jdi.light.elements.complex.Checklist; - -public class MyCheckList extends Checklist { - @Override - public boolean selected(UIElement value) { - return value.hasClass("active") - && value.attr("ui").equals("label"); - } -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/ContactFormPage.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/ContactFormPage.java deleted file mode 100644 index 71286f8..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/ContactFormPage.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.mytests.uiobjects.example.site.pages; - -import com.epam.jdi.light.elements.composite.WebPage; -import com.epam.jdi.light.elements.pageobjects.annotations.Title; -import com.epam.jdi.light.elements.pageobjects.annotations.Url; -import com.epam.jdi.light.elements.pageobjects.annotations.locators.UI; -import org.mytests.uiobjects.example.site.sections.ContactForm; - -@Url("/contacts.html") @Title("Contact Form") -public class ContactFormPage extends WebPage { - @UI("form#contact-form") public static ContactForm contactForm; -} \ No newline at end of file diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/ContactsPage.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/ContactsPage.java deleted file mode 100644 index 21bad2b..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/ContactsPage.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.mytests.uiobjects.example.site.pages; - -import com.epam.jdi.light.elements.complex.dropdown.DropdownSelect; -import com.epam.jdi.light.elements.composite.WebPage; -import com.epam.jdi.light.elements.pageobjects.annotations.Title; -import com.epam.jdi.light.elements.pageobjects.annotations.Url; -import com.epam.jdi.light.elements.pageobjects.annotations.locators.UI; -import com.epam.jdi.light.ui.html.elements.common.Button; -import com.epam.jdi.light.ui.html.elements.common.Checkbox; -import com.epam.jdi.light.ui.html.elements.common.TextArea; -import com.epam.jdi.light.ui.html.elements.common.TextField; -import org.mytests.uiobjects.example.site.custom.MultiDropdown; - -@Url("/contacts.html") @Title("Contact Form") -public class ContactsPage extends WebPage { - TextField firstName, lastName, position, passportNumber, passportSeria; - - DropdownSelect gender; - MultiDropdown weather; - - Checkbox passport, acceptConditions; - TextArea description; - - @UI("['Submit']") public Button submit; -} \ No newline at end of file diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/DatesPage.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/DatesPage.java deleted file mode 100644 index 26da321..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/DatesPage.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.mytests.uiobjects.example.site.pages; - -import com.epam.jdi.light.elements.composite.WebPage; -import com.epam.jdi.light.elements.pageobjects.annotations.Title; -import com.epam.jdi.light.elements.pageobjects.annotations.Url; - -@Url("/dates.html") @Title("Dates") -public class DatesPage extends WebPage { - -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/HomePage.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/HomePage.java deleted file mode 100644 index 7c544b6..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/HomePage.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.mytests.uiobjects.example.site.pages; - -import com.epam.jdi.light.elements.composite.WebPage; -import com.epam.jdi.light.elements.pageobjects.annotations.Title; -import com.epam.jdi.light.elements.pageobjects.annotations.Url; - -@Url("/index.html") @Title("Home Page") -public class HomePage extends WebPage { -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/Html5Page.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/Html5Page.java deleted file mode 100644 index 92d53d5..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/Html5Page.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.mytests.uiobjects.example.site.pages; - -import com.epam.jdi.light.elements.composite.WebPage; -import com.epam.jdi.light.elements.pageobjects.annotations.Title; -import com.epam.jdi.light.elements.pageobjects.annotations.Url; - -@Url("/html5.html") @Title("HTML 5") -public class Html5Page extends WebPage { - -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/JDIPerformancePage.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/JDIPerformancePage.java deleted file mode 100644 index 0c3f40d..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/JDIPerformancePage.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.mytests.uiobjects.example.site.pages; - -import com.epam.jdi.light.elements.complex.dropdown.DropdownSelect; -import com.epam.jdi.light.elements.complex.table.Table; -import com.epam.jdi.light.elements.composite.WebPage; -import com.epam.jdi.light.elements.pageobjects.annotations.Url; -import com.epam.jdi.light.ui.html.elements.common.TextArea; - -@Url("/performance.html") -public class JDIPerformancePage extends WebPage { - // @Css("#users-table") - public static Table usersTable; - - // @Css("#user-names") - public static DropdownSelect userNames; - - //@Css("#textarea-performance") - public static TextArea textareaPerformance; -} diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/UsersPage.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/UsersPage.java deleted file mode 100644 index d3a7fd1..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/pages/UsersPage.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.mytests.uiobjects.example.site.pages; - -import com.epam.jdi.light.elements.complex.table.DataTable; -import com.epam.jdi.light.elements.complex.table.Table; -import com.epam.jdi.light.elements.composite.WebPage; -import com.epam.jdi.light.elements.pageobjects.annotations.Title; -import com.epam.jdi.light.elements.pageobjects.annotations.Url; -import com.epam.jdi.light.elements.pageobjects.annotations.WaitTimeout; -import com.epam.jdi.light.elements.pageobjects.annotations.locators.JTable; -import com.epam.jdi.light.elements.pageobjects.annotations.locators.UI; -import org.mytests.uiobjects.example.custom.MarvelUser; -import org.mytests.uiobjects.example.entities.MarvelUserInfo; - -@Url("/user-table.html") @Title("User Table") -public class UsersPage extends WebPage { - @UI("#user-table") public static Table usersTable; - @UI("#user-table") @WaitTimeout(2) - public static DataTable users; - @UI("#user-table") public static DataTable usersRow; - @JTable(root = "#user-table", rowHeader = "User") - public static DataTable usersSetup; - -} \ No newline at end of file diff --git a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/sections/ContactForm.java b/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/sections/ContactForm.java deleted file mode 100644 index 0d2851a..0000000 --- a/jdi-light-testng/src/main/java/org/mytests/uiobjects/example/site/sections/ContactForm.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.mytests.uiobjects.example.site.sections; - -import com.epam.jdi.light.elements.complex.dropdown.DropdownSelect; -import com.epam.jdi.light.elements.composite.Form; -import com.epam.jdi.light.ui.html.elements.common.Checkbox; -import com.epam.jdi.light.ui.html.elements.common.TextArea; -import com.epam.jdi.light.ui.html.elements.common.TextField; -import org.mytests.uiobjects.example.entities.Contacts; -import org.mytests.uiobjects.example.site.custom.MultiDropdown; - -import java.lang.reflect.Field; - -import static com.jdiai.tools.ReflectionUtils.isInterface; - -public class ContactForm extends Form { - TextField firstName, lastName, position, passportNumber, passportSeria; - - DropdownSelect gender; - MultiDropdown weather; - - Checkbox passport, acceptConditions; - TextArea description; - - //jdi will find [type=submit] element in this context automatically - //@UI("['Submit']") public Button submit; - - @Override - public void fillAction(Field field, Object element, Object parent, String setValue) { - if (isInterface(field, TextField.class)) - ((TextField)element).highlight(); - super.fillAction(field, element, parent, setValue); - } -} \ No newline at end of file diff --git a/jdi-light-testng/src/test/java/org/mytests/tests/TestsInit.java b/jdi-light-testng/src/test/java/org/mytests/tests/TestsInit.java deleted file mode 100644 index 0631d64..0000000 --- a/jdi-light-testng/src/test/java/org/mytests/tests/TestsInit.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.mytests.tests; - -import org.mytests.tests.testng.TestNGListener; -import org.mytests.uiobjects.example.site.SiteJdi; -import org.testng.annotations.AfterSuite; -import org.testng.annotations.BeforeSuite; -import org.testng.annotations.Listeners; - -import static com.epam.jdi.light.driver.WebDriverUtils.killAllSeleniumDrivers; -import static com.epam.jdi.light.elements.composite.WebPage.openSite; -import static com.epam.jdi.light.settings.WebSettings.logger; - -@Listeners(TestNGListener.class) -public interface TestsInit { - @BeforeSuite(alwaysRun = true) - static void setUp() { - killAllSeleniumDrivers(); - openSite(SiteJdi.class); - logger.info("Run Tests"); - } - - @AfterSuite(alwaysRun = true) - static void teardown() { - killAllSeleniumDrivers(); - } -} diff --git a/jdi-light-testng/src/test/java/org/mytests/tests/example/DataTableInteractTests.java b/jdi-light-testng/src/test/java/org/mytests/tests/example/DataTableInteractTests.java deleted file mode 100644 index cd9cb4e..0000000 --- a/jdi-light-testng/src/test/java/org/mytests/tests/example/DataTableInteractTests.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.mytests.tests.example; - -import io.visual_regression_tracker.sdk_java.VisualRegressionTracker; -import io.visual_regression_tracker.sdk_java.VisualRegressionTrackerConfig; -import org.mytests.tests.TestsInit; -import org.mytests.tests.testng.TestNGListener; -import org.openqa.selenium.OutputType; -import org.openqa.selenium.TakesScreenshot; -import org.testng.annotations.*; - -import java.io.IOException; - -import static com.epam.jdi.light.driver.WebDriverFactory.getDriver; -import static org.mytests.tests.states.States.shouldBeLoggedIn; -import static org.mytests.uiobjects.example.site.SiteJdi.usersPage; - -@Listeners(TestNGListener.class) -public class DataTableInteractTests implements TestsInit { - static VisualRegressionTracker vrt; - static VisualRegressionTrackerConfig config = VisualRegressionTrackerConfig.builder() - .apiUrl("http://localhost:4200") - .apiKey("DEFAULTUSERAPIKEYTOBECHANGED") - .project("Default project") - .branchName("master") - .enableSoftAssert(true) - .build(); - - @BeforeClass - public static void setUp() throws IOException, InterruptedException { - vrt = new VisualRegressionTracker(config); - vrt.start(); - } - - @AfterClass - public static void tearDown() throws IOException, InterruptedException { - vrt.stop(); - } - - @BeforeMethod - public void before() { - shouldBeLoggedIn(); - usersPage.shouldBeOpened(); - } - - @Test - public void shouldUsersPageLookSame() throws IOException, InterruptedException { - vrt.track("Users page", - ((TakesScreenshot) getDriver()).getScreenshotAs(OutputType.BASE64)); - } -} diff --git a/jdi-light-testng/src/test/java/org/mytests/tests/states/States.java b/jdi-light-testng/src/test/java/org/mytests/tests/states/States.java deleted file mode 100644 index 1c96e1c..0000000 --- a/jdi-light-testng/src/test/java/org/mytests/tests/states/States.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.mytests.tests.states; - -import com.epam.jdi.light.elements.composite.WebPage; -import io.qameta.allure.Step; -import org.mytests.uiobjects.example.entities.User; - -import static org.mytests.uiobjects.example.site.SiteJdi.*; - -/** - * Created by Roman_Iovlev on 3/1/2018. - */ -public class States { - private static void onSite() { - if (!WebPage.getUrl().contains("https://jdi-testing.github.io/jdi-light/")) - homePage.open(); - } - @Step - public static void shouldBeLoggedIn() { - onSite(); - if (!userName.isDisplayed()) - login(); - } - @Step - public static void login() { - userIcon.click(); - loginForm.submit(new User(), "enter"); - } - @Step - public static void shouldBeLoggedOut() { - onSite(); - if (userName.isDisplayed()) - logout(); - if (loginForm.isDisplayed()) - userIcon.click(); - } - @Step - public static void logout() { - if (!logout.isDisplayed()) - userIcon.click(); - logout.click(); - } -} diff --git a/jdi-light-testng/src/test/java/org/mytests/tests/testng/RetryFailedTestCases.java b/jdi-light-testng/src/test/java/org/mytests/tests/testng/RetryFailedTestCases.java deleted file mode 100644 index b832bfc..0000000 --- a/jdi-light-testng/src/test/java/org/mytests/tests/testng/RetryFailedTestCases.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.mytests.tests.testng; - -import org.testng.IRetryAnalyzer; -import org.testng.ITestResult; - -public class RetryFailedTestCases implements IRetryAnalyzer { - private int retryCnt = 0; - - //This method will be called everytime a test fails. It will return TRUE if a test fails and need to be retried, else it returns FALSE - public boolean retry(ITestResult result) { - //You could mentioned maxRetryCnt (Maximiun Retry Count) as per your requirement. Here I took 2, If any failed testcases then it runs two times - int maxRetryCnt = 1; - if (retryCnt < maxRetryCnt) { - System.out.println("Retrying " + result.getName() + " again and the count is " + (retryCnt+1)); - retryCnt++; - return true; - } - return false; - } - -} - diff --git a/jdi-light-testng/src/test/java/org/mytests/tests/testng/RetryListenerClass.java b/jdi-light-testng/src/test/java/org/mytests/tests/testng/RetryListenerClass.java deleted file mode 100644 index f209aee..0000000 --- a/jdi-light-testng/src/test/java/org/mytests/tests/testng/RetryListenerClass.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.mytests.tests.testng; - -import org.testng.IAnnotationTransformer; -import org.testng.annotations.ITestAnnotation; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; - -public class RetryListenerClass implements IAnnotationTransformer { - - @Override - public void transform(ITestAnnotation testannotation, Class testClass, Constructor testConstructor, Method testMethod) { - testannotation.setRetryAnalyzer(RetryFailedTestCases.class); - } -} diff --git a/jdi-light-testng/src/test/java/org/mytests/tests/testng/TestNGListener.java b/jdi-light-testng/src/test/java/org/mytests/tests/testng/TestNGListener.java deleted file mode 100644 index 74e508e..0000000 --- a/jdi-light-testng/src/test/java/org/mytests/tests/testng/TestNGListener.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.mytests.tests.testng; - -/** - * Created by Roman Iovlev on 14.02.2018 - * Email: roman.iovlev.jdi@gmail.com; Skype: roman.iovlev - */ - -import com.jdiai.tools.Safe; -import org.testng.IInvokedMethod; -import org.testng.IInvokedMethodListener; -import org.testng.ITestNGMethod; -import org.testng.ITestResult; -import org.testng.annotations.Test; - -import java.text.SimpleDateFormat; -import java.util.Date; - -import static com.epam.jdi.light.driver.ScreenshotMaker.takeScreen; -import static com.epam.jdi.light.settings.WebSettings.TEST_NAME; -import static com.epam.jdi.light.settings.WebSettings.logger; -import static com.jdiai.tools.LinqUtils.last; -import static java.lang.System.currentTimeMillis; - -public class TestNGListener implements IInvokedMethodListener { private Safe start = new Safe<>(0L); - - @Override - public void beforeInvocation(IInvokedMethod m, ITestResult tr) { - if (m.isTestMethod()) { - ITestNGMethod testMethod = m.getTestMethod(); - if (testMethod.getConstructorOrMethod().getMethod().isAnnotationPresent(Test.class)) { - TEST_NAME.set( last(testMethod.getTestClass().getName().split("\\.")) + - "." + testMethod.getMethodName()); - start.set(currentTimeMillis()); - logger.step("== Test '%s' START ==", TEST_NAME.get()); - } - } - } - - @Override - public void afterInvocation(IInvokedMethod method, ITestResult tr) { - if (method.isTestMethod()) { - String result = getTestResult(tr); - logger.step("=== Test '%s' %s [%s] ===", TEST_NAME.get(), result, - new SimpleDateFormat("mm:ss.SS") - .format(new Date(currentTimeMillis() - start.get()))); - if ("FAILED".equals(result)) { - try { - takeScreen(); - } catch (RuntimeException ignored) { } - if (tr.getThrowable() != null) { - logger.step("ERROR: " + tr.getThrowable().getMessage()); - } else { - logger.step("UNKNOWN ERROR"); - } - } - } - } - - private String getTestResult(ITestResult result) { - switch (result.getStatus()) { - case ITestResult.SUCCESS: - return "PASSED"; - case ITestResult.SKIP: - return "SKIPPED"; - default: - return "FAILED"; - } - } -} \ No newline at end of file diff --git a/jdi-light-testng/src/test/resources/META-INF/services/org.testng.ITestNGListener b/jdi-light-testng/src/test/resources/META-INF/services/org.testng.ITestNGListener deleted file mode 100644 index c9e8f5a..0000000 --- a/jdi-light-testng/src/test/resources/META-INF/services/org.testng.ITestNGListener +++ /dev/null @@ -1 +0,0 @@ -org.mytests.tests.testng.RetryListenerClass diff --git a/jdi-light-testng/src/test/resources/chrome.properties b/jdi-light-testng/src/test/resources/chrome.properties deleted file mode 100644 index b4f7d89..0000000 --- a/jdi-light-testng/src/test/resources/chrome.properties +++ /dev/null @@ -1,2 +0,0 @@ -#arguments=--ignore-certificate-errors --start-maximized --incognito -#enableVNC=true \ No newline at end of file diff --git a/jdi-light-testng/src/test/resources/log4j2.xml b/jdi-light-testng/src/test/resources/log4j2.xml deleted file mode 100644 index 6b7aec5..0000000 --- a/jdi-light-testng/src/test/resources/log4j2.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - jdiEvents - ./target/.logs - - - - - - - - - ${baseDir}/${logFileName}_%d{yyyy-MM-dd__HH-mm}_%i.log - - %d{HH:mm:ss} [%t] %-5level %logger{6} - %msg%n - - - - - - - - - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/jdi-light-testng/src/test/resources/test.properties b/jdi-light-testng/src/test/resources/test.properties deleted file mode 100644 index 5fe324f..0000000 --- a/jdi-light-testng/src/test/resources/test.properties +++ /dev/null @@ -1,33 +0,0 @@ -driver=${driver} -#driver.version=2.23 -timeout.wait.element=10 -domain=https://jdi-testing.github.io/jdi-light/ -page.check.after.open=NONE -# NEW_PAGE | EVERY_PAGE -#element.search.strategy=strict | soft | visible, multiple | any, single -element.search.strategy=strict -smart.locator=#%s -#smart.locator.to.name=kebab-case | camelCase | snake_case | PascalCase | UPPER_SNAKE_CASE -log.level=INFO -log.info.details=element -#text.type=SMART_TEXT | TEXT | VALUE | INNER | LABEL -#set.text.type=SET_TEXT | SEND_KEYS | CLEAR_SEND_KEYS -#assert.type=strict | soft - -#page.load.strategy=normal -#browser.size=MAXIMIZE | 1024x762 -#browser.kill=afterAndBefore | after | before -#drivers.folder=C:\\Selenium -#log.message.format=short | full -#demo.mode=false | true -#demo.delay=2 -#remote.type=sauce | browserstack -#driver.remote.url=http://localhost:4444/wd/hub -headless=true -screenshot.strategy=on failure -#element.highlight=off | on failure | on assert | on action | flow -#screenshot.strategy=off | on failure | on assert | new page | flow -#allure.steps=true -#html.code.strategy=off | on failure | on assert | new page -#requests.strategy=off | on failure | on assert | new page -#screenshot.tool=selenium | robot \ No newline at end of file diff --git a/playwright/README.md b/playwright/README.md new file mode 100644 index 0000000..5684c96 --- /dev/null +++ b/playwright/README.md @@ -0,0 +1,29 @@ +# Playwright + Maven + JUnit 4 + +[PlaywrightExampleTest.java](src/test/java/PlaywrightExampleTest.java) opens https://the-internet.herokuapp.com headless with [Playwright for Java](https://playwright.dev/java/) and tracks two screenshots with [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java), the second with test run options and an ignore area. + +## Prerequisites + +- JDK 17 or later and Maven +- A running [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker) 6.0 server and its API key + +## Setup + +- Copy `vrt.json.example` to `vrt.json` and set your connection, or use the [`VRT_*` environment variables](https://github.com/Visual-Regression-Tracker/sdk-java#configuration), which override the file + +Playwright downloads its browsers on the first run. + +## Run + +```sh +mvn test +``` + +## What to expect + +`enableSoftAssert` is on: the SDK logs a failed check through SLF4J instead of throwing, so the test passes. The examples have no SLF4J binding, so the log stays silent; add one, such as `slf4j-simple`, to see it. + +- First run: the VRT UI shows a build with two new test runs, "Home page" and "Checkboxes page". Approve them to make them the baseline for the branch. +- Next runs: the test runs are `Ok` while the pages look the same. When one changes, it is `Unresolved`, and the build shows the diff to approve or reject. + +Set `enableSoftAssert` to `false` to make new and changed screenshots fail the test. diff --git a/playwright/pom.xml b/playwright/pom.xml index 1d1516e..bca4f75 100644 --- a/playwright/pom.xml +++ b/playwright/pom.xml @@ -44,7 +44,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.15.0 + 3.16.0 org.apache.maven.plugins diff --git a/playwright/src/test/java/PlaywrightExampleTest.java b/playwright/src/test/java/PlaywrightExampleTest.java index 4d7b789..001d5cd 100644 --- a/playwright/src/test/java/PlaywrightExampleTest.java +++ b/playwright/src/test/java/PlaywrightExampleTest.java @@ -9,19 +9,13 @@ import org.junit.BeforeClass; import org.junit.Test; +import java.io.File; import java.io.IOException; import java.util.Base64; import java.util.List; public class PlaywrightExampleTest { - static VisualRegressionTracker vrt = new VisualRegressionTracker( - VisualRegressionTrackerConfig.builder() - .apiUrl("http://localhost:4200") - .apiKey("DEFAULTUSERAPIKEYTOBECHANGED") - .project("Default project") - .branchName("master") - .enableSoftAssert(true) - .build()); + static VisualRegressionTracker vrt = new VisualRegressionTracker(vrtConfig()); static Playwright playwright; static Browser browser; static Page page; @@ -66,4 +60,13 @@ public void example() throws IOException, InterruptedException { .build())) .build()); } + + static VisualRegressionTrackerConfig vrtConfig() { + VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder builder = VisualRegressionTrackerConfig.builder(); + File configFile = new File("vrt.json"); + if (configFile.exists()) { + builder.configFile(configFile); + } + return builder.build(); + } } diff --git a/playwright/vrt.json.example b/playwright/vrt.json.example new file mode 100644 index 0000000..c372ab5 --- /dev/null +++ b/playwright/vrt.json.example @@ -0,0 +1,7 @@ +{ + "apiUrl": "http://localhost:4200", + "apiKey": "DEFAULTUSERAPIKEYTOBECHANGED", + "project": "Default project", + "branchName": "master", + "enableSoftAssert": true +} diff --git a/selenide-gradle/README.md b/selenide-gradle/README.md new file mode 100644 index 0000000..da0a587 --- /dev/null +++ b/selenide-gradle/README.md @@ -0,0 +1,28 @@ +# Selenide + Gradle + TestNG + +[SelenideExample.java](src/test/java/SelenideExample.java) opens https://the-internet.herokuapp.com in headless Chrome with [Selenide](https://selenide.org) and tracks two screenshots with [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java), the second with test run options and an ignore area. + +## Prerequisites + +- JDK 17 or later; the Gradle wrapper downloads Gradle +- Google Chrome; Selenium Manager downloads a matching chromedriver +- A running [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker) 6.0 server and its API key + +## Setup + +- Copy `vrt.json.example` to `vrt.json` and set your connection, or use the [`VRT_*` environment variables](https://github.com/Visual-Regression-Tracker/sdk-java#configuration), which override the file + +## Run + +```sh +./gradlew test +``` + +## What to expect + +`enableSoftAssert` is on: the SDK logs a failed check through SLF4J instead of throwing, so the test passes. The examples have no SLF4J binding, so the log stays silent; add one, such as `slf4j-simple`, to see it. + +- First run: the VRT UI shows a build with two new test runs, "Home page" and "Checkboxes page". Approve them to make them the baseline for the branch. +- Next runs: the test runs are `Ok` while the pages look the same. When one changes, it is `Unresolved`, and the build shows the diff to approve or reject. + +Set `enableSoftAssert` to `false` to make new and changed screenshots fail the test. diff --git a/selenide-gradle/src/test/java/SelenideExample.java b/selenide-gradle/src/test/java/SelenideExample.java index af19f26..77f3d67 100644 --- a/selenide-gradle/src/test/java/SelenideExample.java +++ b/selenide-gradle/src/test/java/SelenideExample.java @@ -9,22 +9,14 @@ import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; +import java.io.File; import java.io.IOException; import java.util.List; import static com.codeborne.selenide.Selenide.$; public class SelenideExample { - VisualRegressionTrackerConfig config = VisualRegressionTrackerConfig.builder() - .apiUrl("http://localhost:4200") - .apiKey("DEFAULTUSERAPIKEYTOBECHANGED") - .project("Default project") - .branchName("master") - .enableSoftAssert(true) - .httpTimeoutInSeconds(15) - .build(); - - VisualRegressionTracker vrt = new VisualRegressionTracker(config); + VisualRegressionTracker vrt = new VisualRegressionTracker(vrtConfig()); @BeforeSuite public void setUp() throws IOException, InterruptedException { @@ -68,4 +60,13 @@ public void testExample() throws IOException, InterruptedException { .build())) .build()); } + + static VisualRegressionTrackerConfig vrtConfig() { + VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder builder = VisualRegressionTrackerConfig.builder(); + File configFile = new File("vrt.json"); + if (configFile.exists()) { + builder.configFile(configFile); + } + return builder.build(); + } } diff --git a/selenide-gradle/vrt.json.example b/selenide-gradle/vrt.json.example new file mode 100644 index 0000000..c372ab5 --- /dev/null +++ b/selenide-gradle/vrt.json.example @@ -0,0 +1,7 @@ +{ + "apiUrl": "http://localhost:4200", + "apiKey": "DEFAULTUSERAPIKEYTOBECHANGED", + "project": "Default project", + "branchName": "master", + "enableSoftAssert": true +} diff --git a/selenium-maven/README.md b/selenium-maven/README.md new file mode 100644 index 0000000..34ed581 --- /dev/null +++ b/selenium-maven/README.md @@ -0,0 +1,28 @@ +# Selenium 4 + Maven + JUnit 4 + +[SeleniumExampleTest.java](src/test/java/com/example/test/SeleniumExampleTest.java) opens https://the-internet.herokuapp.com in headless Chrome with [Selenium](https://www.selenium.dev/documentation/webdriver/) and tracks two screenshots with [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java), the second with test run options and an ignore area. + +## Prerequisites + +- JDK 17 or later and Maven +- Google Chrome; Selenium Manager downloads a matching chromedriver +- A running [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker) 6.0 server and its API key + +## Setup + +- Copy `vrt.json.example` to `vrt.json` and set your connection, or use the [`VRT_*` environment variables](https://github.com/Visual-Regression-Tracker/sdk-java#configuration), which override the file + +## Run + +```sh +mvn test +``` + +## What to expect + +`enableSoftAssert` is on: the SDK logs a failed check through SLF4J instead of throwing, so the test passes. The examples have no SLF4J binding, so the log stays silent; add one, such as `slf4j-simple`, to see it. + +- First run: the VRT UI shows a build with two new test runs, "Home page" and "Checkboxes page". Approve them to make them the baseline for the branch. +- Next runs: the test runs are `Ok` while the pages look the same. When one changes, it is `Unresolved`, and the build shows the diff to approve or reject. + +Set `enableSoftAssert` to `false` to make new and changed screenshots fail the test. diff --git a/selenium-maven/pom.xml b/selenium-maven/pom.xml index 53d055a..cfea35d 100644 --- a/selenium-maven/pom.xml +++ b/selenium-maven/pom.xml @@ -44,7 +44,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.15.0 + 3.16.0 org.apache.maven.plugins diff --git a/selenium-maven/src/test/java/com/example/test/SeleniumExampleTest.java b/selenium-maven/src/test/java/com/example/test/SeleniumExampleTest.java index 72e674e..2634598 100644 --- a/selenium-maven/src/test/java/com/example/test/SeleniumExampleTest.java +++ b/selenium-maven/src/test/java/com/example/test/SeleniumExampleTest.java @@ -14,25 +14,19 @@ import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; +import java.io.File; import java.io.IOException; import java.util.List; public class SeleniumExampleTest { static VisualRegressionTracker vrt; static ChromeDriver driver; - static VisualRegressionTrackerConfig config = VisualRegressionTrackerConfig.builder() - .apiUrl("http://localhost:4200") - .apiKey("DEFAULTUSERAPIKEYTOBECHANGED") - .project("Default project") - .branchName("master") - .enableSoftAssert(true) - .build(); @BeforeClass public static void setUp() throws IOException, InterruptedException { driver = new ChromeDriver(new ChromeOptions().addArguments("--headless=new")); driver.manage().window().setSize(new Dimension(1280, 1024)); - vrt = new VisualRegressionTracker(config); + vrt = new VisualRegressionTracker(vrtConfig()); vrt.start(); } @@ -68,4 +62,13 @@ public void exampleTest() throws IOException, InterruptedException { .build())) .build()); } + + static VisualRegressionTrackerConfig vrtConfig() { + VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder builder = VisualRegressionTrackerConfig.builder(); + File configFile = new File("vrt.json"); + if (configFile.exists()) { + builder.configFile(configFile); + } + return builder.build(); + } } diff --git a/selenium-maven/vrt.json.example b/selenium-maven/vrt.json.example new file mode 100644 index 0000000..c372ab5 --- /dev/null +++ b/selenium-maven/vrt.json.example @@ -0,0 +1,7 @@ +{ + "apiUrl": "http://localhost:4200", + "apiKey": "DEFAULTUSERAPIKEYTOBECHANGED", + "project": "Default project", + "branchName": "master", + "enableSoftAssert": true +} diff --git a/standalone-from-folder-maven/.gitignore b/standalone-from-folder-maven/.gitignore index 26a9bfe..9ed3e95 100644 --- a/standalone-from-folder-maven/.gitignore +++ b/standalone-from-folder-maven/.gitignore @@ -1,2 +1,3 @@ *.iml target +vrt_result.html diff --git a/standalone-from-folder-maven/README.md b/standalone-from-folder-maven/README.md index 3b180ba..719e438 100644 --- a/standalone-from-folder-maven/README.md +++ b/standalone-from-folder-maven/README.md @@ -1,15 +1,32 @@ -# Compare image and pdf files from a folder -This can be used to compare existing PNG and PDF files in a folder. Each page of PDF file is converted to ```BufferedImage``` and then compared in VRT. +# Compare PNG and PDF files from a folder -#### Use it as a standalone executable jar file passing parameters from command line -- Make a fat jar by giving below maven command +[CompareFromFile.java](src/main/java/CompareFromFile.java) is a command-line client: it tracks every PNG file in a folder, and every page of every PDF file as a PNG, with [sdk-java](https://github.com/Visual-Regression-Tracker/sdk-java). Use it to compare images that another tool produced. - ```mvn clean compile assembly:single``` -- Then run the jar file in command line as +CI only compiles this example: it needs a folder of images. - ```java -jar vrt-standalone-client-1.0.1.jar http://localhost:4200 CDJ3HHD5MY45G0PK5M3PBB6NBGC9 c936bfa4-50b3-40c4-b177-94efbeabfbcf null main ``` -- You can see help by running without parameters like below. +## Prerequisites - ```java -jar vrt-standalone-client-1.0.1.jar``` +- JDK 17 or later and Maven +- A running [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker) 6.0 server and its API key -#### You can also use hardcoded values for the VRT parameters in the code and run from IDE. +## Setup + +- Copy `vrt.json.example` to `vrt.json` in the folder you run the client from and set your connection, or use the [`VRT_*` environment variables](https://github.com/Visual-Regression-Tracker/sdk-java#configuration), which override the file +- Build a jar with its dependencies: `mvn compile assembly:single` + +## Run + +```sh +java -jar target/vrt-standalone-client-1.0.1.jar +``` + +Without ``, the client reads the current folder. You can also run `CompareFromFile` from your IDE. + +## What to expect + +The client prints the build status and a summary: how many files it processed, how many runs of each status, and whether all passed. It also writes the counts as a chart to `vrt_result.html` in the current folder. + +- First run: every image is `New`. The VRT UI shows a build with one test run per PNG file and per PDF page, named after the file (`report_Page1.png` for the first page of `report.pdf`). Approve them to make them the baseline for the branch. +- Next runs: `Ok` while the images are the same. When one changes, it is `Unresolved`, and the build shows the diff to approve or reject. + +`enableSoftAssert` only decides whether the SDK logs or throws; the client handles both, and counts new and changed images as not passed. diff --git a/standalone-from-folder-maven/pom.xml b/standalone-from-folder-maven/pom.xml index 09a1820..b57f8ca 100644 --- a/standalone-from-folder-maven/pom.xml +++ b/standalone-from-folder-maven/pom.xml @@ -43,7 +43,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.15.0 + 3.16.0 org.apache.maven.plugins diff --git a/standalone-from-folder-maven/src/main/java/CompareFromFile.java b/standalone-from-folder-maven/src/main/java/CompareFromFile.java index 1b51a52..32ca8f6 100644 --- a/standalone-from-folder-maven/src/main/java/CompareFromFile.java +++ b/standalone-from-folder-maven/src/main/java/CompareFromFile.java @@ -18,68 +18,26 @@ public class CompareFromFile { - static final String ANSI_BLACK = "\u001B[30m"; - static final String ANSI_RED = "\u001B[31m"; static final String ANSI_GREEN = "\u001B[32m"; - static final String ANSI_YELLOW = "\u001B[33m"; static final String ANSI_RESET = "\u001B[0m"; - static final String ANSI_PURPLE = "\u001B[35m"; - static final String ANSI_CYAN = "\u001B[36m"; static final String ANSI_BLUE = "\u001B[34m"; - static final String ANSI_WHITE = "\u001B[37m"; static final String BOLD_TEXT = "\033[0;1m"; public static void main(String[] args) { - if (args.length == 0 || args[0].trim().contentEquals("help")) { - System.out.println( - BOLD_TEXT + ANSI_PURPLE + "Pass arguments in the following order" + ANSI_RESET + System.lineSeparator() + - BOLD_TEXT + ANSI_GREEN + "API URL " + ANSI_RESET + ANSI_CYAN + "(mandatory)" + ANSI_RESET + " - This is the url of VRT backend." + System.lineSeparator() + - BOLD_TEXT + ANSI_GREEN + "API Key " + ANSI_RESET + ANSI_CYAN + "(mandatory)" + ANSI_RESET + " - Get it from VRT UI." + System.lineSeparator() + - BOLD_TEXT + ANSI_GREEN + "Project " + ANSI_RESET + ANSI_CYAN + "(mandatory)" + ANSI_RESET + " - Get it from VRT UI." + System.lineSeparator() + - BOLD_TEXT + ANSI_GREEN + "CI Build Id " + ANSI_RESET + ANSI_CYAN + "(optional)" + ANSI_RESET + " - Use it to tag a VRT build with Jenkins build id. Pass null if you want to skip it and use the next parameter." + System.lineSeparator() + - BOLD_TEXT + ANSI_GREEN + "Branch " + ANSI_RESET + ANSI_CYAN + "(optional for VRT higher than 4.19.0)" + ANSI_RESET + " - Will take default branch defined in VRT for the project. Pass null if you want to skip it and use the next parameter." + System.lineSeparator() + - BOLD_TEXT + ANSI_GREEN + "File Path " + ANSI_RESET + ANSI_CYAN + "(optional)" + ANSI_RESET + " - Give full path. It will take current folder if not provided." + System.lineSeparator() + - BOLD_TEXT + ANSI_YELLOW + "Example: " + ANSI_RESET + ANSI_YELLOW + "java -jar vrt-standalone-client-1.0.0.jar http://localhost:4200 CDJ3HHD5MY45G0PK5M3PBB6NBGC9 c936bfa4-50b3-40c4-b177-94efbeabfbcf null main" + ANSI_RESET); - System.exit(0); - } - if (args.length < 2) { - System.out.println("You have to pass apiUrl, apiKey and project at a minimum." + System.lineSeparator() + - "Example: java -jar vrt-standalone-client-1.0.0.jar http://localhost:4200 CDJ3HHD5MY45G0PK5M3PBB6NBGC9 c936bfa4-50b3-40c4-b177-94efbeabfbcf null main"); - System.exit(0); - } - - String apiUrl = args[0]; - //String apiUrl = "http://localhost:4200"; - String apiKey = args[1]; - //String apiKey = "CDJ3HHD5MY45G0PK5M3PBB6NBGC9"; //Populate your key - String project = args[2]; - //String project = "c936bfa4-50b3-40c4-b177-94efbeabfbcf"; // Populate your project - String ciBuildId = args.length > 3 ? args[3] : null; - //String ciBuildId = null; - String branch = args.length > 4 ? args[4] : null; - //String branch = "main"; - String filePath = args.length > 5 ? args[5] : null; - //String filePath = ".";//Give complete path of the image files or . for current path. - - filePath = filePath == null ? "." : filePath; - runVRT(apiUrl, apiKey, project, ciBuildId, branch, filePath); + runVRT(args.length > 0 ? args[0] : "."); } - private static void runVRT(String apiUrl, String apiKey, String project, String ciBuildId, String branch, String filePath) { - VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder configBuilder = VisualRegressionTrackerConfig.builder() - .apiUrl(apiUrl) - .apiKey(apiKey) - .project(project); - - if (ciBuildId != null && !ciBuildId.contentEquals("null")) - configBuilder.ciBuildId(ciBuildId); - //If you use VRT version 4.19.0 or lower, then branch is mandatory - //For higher versions, branch name will default to the main branch if not provided here. - if (branch != null && !branch.contentEquals("null")) - configBuilder.branchName(branch); - VisualRegressionTrackerConfig config = configBuilder.build(); + static VisualRegressionTrackerConfig vrtConfig() { + VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder builder = VisualRegressionTrackerConfig.builder(); + File configFile = new File("vrt.json"); + if (configFile.exists()) { + builder.configFile(configFile); + } + return builder.build(); + } - VisualRegressionTracker visualRegressionTracker = new VisualRegressionTracker(config); + private static void runVRT(String filePath) { + VisualRegressionTracker visualRegressionTracker = new VisualRegressionTracker(vrtConfig()); Map results = new HashMap<>(); results.put("allImageVerified", true); results.put("allPassed", true); diff --git a/standalone-from-folder-maven/vrt.json.example b/standalone-from-folder-maven/vrt.json.example new file mode 100644 index 0000000..c372ab5 --- /dev/null +++ b/standalone-from-folder-maven/vrt.json.example @@ -0,0 +1,7 @@ +{ + "apiUrl": "http://localhost:4200", + "apiKey": "DEFAULTUSERAPIKEYTOBECHANGED", + "project": "Default project", + "branchName": "master", + "enableSoftAssert": true +} From df95bd9d37ea095effaa2596eae247d3a53883c2 Mon Sep 17 00:00:00 2001 From: Pavlo Strunkin Date: Mon, 2 Nov 2026 22:56:39 +0200 Subject: [PATCH 2/3] refactor: use the SDK's default config loading, drop dead code Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 19 +++----- .../test/java/IOSBasicInteractionsTest.java | 12 +---- .../src/test/java/PlaywrightExampleTest.java | 13 +----- .../src/test/java/SelenideExample.java | 13 +----- .../com/example/test/SeleniumExampleTest.java | 13 +----- .../src/main/java/CompareFromFile.java | 45 +------------------ 6 files changed, 12 insertions(+), 103 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85e9c9a..ffc6897 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,13 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + VRT_APIURL: http://localhost:4200 + VRT_APIKEY: DEFAULTUSERAPIKEYTOBECHANGED + VRT_PROJECT: Default project + VRT_BRANCHNAME: master + VRT_ENABLESOFTASSERT: "true" + jobs: maven: name: ${{ matrix.example }} @@ -23,12 +30,6 @@ jobs: defaults: run: working-directory: ${{ matrix.example }} - env: - VRT_APIURL: http://localhost:4200 - VRT_APIKEY: DEFAULTUSERAPIKEYTOBECHANGED - VRT_PROJECT: Default project - VRT_BRANCHNAME: master - VRT_ENABLESOFTASSERT: "true" steps: - uses: actions/checkout@v7 - name: Start Visual Regression Tracker @@ -55,12 +56,6 @@ jobs: defaults: run: working-directory: ${{ matrix.example }} - env: - VRT_APIURL: http://localhost:4200 - VRT_APIKEY: DEFAULTUSERAPIKEYTOBECHANGED - VRT_PROJECT: Default project - VRT_BRANCHNAME: master - VRT_ENABLESOFTASSERT: "true" steps: - uses: actions/checkout@v7 - name: Start Visual Regression Tracker diff --git a/appium-maven/src/test/java/IOSBasicInteractionsTest.java b/appium-maven/src/test/java/IOSBasicInteractionsTest.java index 433448e..e100aef 100644 --- a/appium-maven/src/test/java/IOSBasicInteractionsTest.java +++ b/appium-maven/src/test/java/IOSBasicInteractionsTest.java @@ -12,7 +12,6 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.io.File; import java.io.IOException; import java.time.Duration; import java.util.Objects; @@ -34,7 +33,7 @@ public void setUp() throws IOException, InterruptedException { driver = new IOSDriver(getServiceUrl(), options); - vrt = new VisualRegressionTracker(vrtConfig()); + vrt = new VisualRegressionTracker(); vrt.start(); } @@ -99,13 +98,4 @@ void vrtTrackWithRetry(String pageName, TestRunOptions testRunOptions, int maxRe vrtTrackWithRetry(pageName, testRunOptions, maxRetries - 1); } - - static VisualRegressionTrackerConfig vrtConfig() { - VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder builder = VisualRegressionTrackerConfig.builder(); - File configFile = new File("vrt.json"); - if (configFile.exists()) { - builder.configFile(configFile); - } - return builder.build(); - } } diff --git a/playwright/src/test/java/PlaywrightExampleTest.java b/playwright/src/test/java/PlaywrightExampleTest.java index 001d5cd..eda6f68 100644 --- a/playwright/src/test/java/PlaywrightExampleTest.java +++ b/playwright/src/test/java/PlaywrightExampleTest.java @@ -4,18 +4,16 @@ import io.visual_regression_tracker.sdk_java.IgnoreAreas; import io.visual_regression_tracker.sdk_java.TestRunOptions; import io.visual_regression_tracker.sdk_java.VisualRegressionTracker; -import io.visual_regression_tracker.sdk_java.VisualRegressionTrackerConfig; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import java.io.File; import java.io.IOException; import java.util.Base64; import java.util.List; public class PlaywrightExampleTest { - static VisualRegressionTracker vrt = new VisualRegressionTracker(vrtConfig()); + static VisualRegressionTracker vrt = new VisualRegressionTracker(); static Playwright playwright; static Browser browser; static Page page; @@ -60,13 +58,4 @@ public void example() throws IOException, InterruptedException { .build())) .build()); } - - static VisualRegressionTrackerConfig vrtConfig() { - VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder builder = VisualRegressionTrackerConfig.builder(); - File configFile = new File("vrt.json"); - if (configFile.exists()) { - builder.configFile(configFile); - } - return builder.build(); - } } diff --git a/selenide-gradle/src/test/java/SelenideExample.java b/selenide-gradle/src/test/java/SelenideExample.java index 77f3d67..b5f167e 100644 --- a/selenide-gradle/src/test/java/SelenideExample.java +++ b/selenide-gradle/src/test/java/SelenideExample.java @@ -3,20 +3,18 @@ import io.visual_regression_tracker.sdk_java.IgnoreAreas; import io.visual_regression_tracker.sdk_java.TestRunOptions; import io.visual_regression_tracker.sdk_java.VisualRegressionTracker; -import io.visual_regression_tracker.sdk_java.VisualRegressionTrackerConfig; import org.openqa.selenium.OutputType; import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; -import java.io.File; import java.io.IOException; import java.util.List; import static com.codeborne.selenide.Selenide.$; public class SelenideExample { - VisualRegressionTracker vrt = new VisualRegressionTracker(vrtConfig()); + VisualRegressionTracker vrt = new VisualRegressionTracker(); @BeforeSuite public void setUp() throws IOException, InterruptedException { @@ -60,13 +58,4 @@ public void testExample() throws IOException, InterruptedException { .build())) .build()); } - - static VisualRegressionTrackerConfig vrtConfig() { - VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder builder = VisualRegressionTrackerConfig.builder(); - File configFile = new File("vrt.json"); - if (configFile.exists()) { - builder.configFile(configFile); - } - return builder.build(); - } } diff --git a/selenium-maven/src/test/java/com/example/test/SeleniumExampleTest.java b/selenium-maven/src/test/java/com/example/test/SeleniumExampleTest.java index 2634598..2f5192c 100644 --- a/selenium-maven/src/test/java/com/example/test/SeleniumExampleTest.java +++ b/selenium-maven/src/test/java/com/example/test/SeleniumExampleTest.java @@ -3,7 +3,6 @@ import io.visual_regression_tracker.sdk_java.IgnoreAreas; import io.visual_regression_tracker.sdk_java.TestRunOptions; import io.visual_regression_tracker.sdk_java.VisualRegressionTracker; -import io.visual_regression_tracker.sdk_java.VisualRegressionTrackerConfig; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -14,7 +13,6 @@ import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; -import java.io.File; import java.io.IOException; import java.util.List; @@ -26,7 +24,7 @@ public class SeleniumExampleTest { public static void setUp() throws IOException, InterruptedException { driver = new ChromeDriver(new ChromeOptions().addArguments("--headless=new")); driver.manage().window().setSize(new Dimension(1280, 1024)); - vrt = new VisualRegressionTracker(vrtConfig()); + vrt = new VisualRegressionTracker(); vrt.start(); } @@ -62,13 +60,4 @@ public void exampleTest() throws IOException, InterruptedException { .build())) .build()); } - - static VisualRegressionTrackerConfig vrtConfig() { - VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder builder = VisualRegressionTrackerConfig.builder(); - File configFile = new File("vrt.json"); - if (configFile.exists()) { - builder.configFile(configFile); - } - return builder.build(); - } } diff --git a/standalone-from-folder-maven/src/main/java/CompareFromFile.java b/standalone-from-folder-maven/src/main/java/CompareFromFile.java index 32ca8f6..8f69c47 100644 --- a/standalone-from-folder-maven/src/main/java/CompareFromFile.java +++ b/standalone-from-folder-maven/src/main/java/CompareFromFile.java @@ -27,17 +27,8 @@ public static void main(String[] args) { runVRT(args.length > 0 ? args[0] : "."); } - static VisualRegressionTrackerConfig vrtConfig() { - VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder builder = VisualRegressionTrackerConfig.builder(); - File configFile = new File("vrt.json"); - if (configFile.exists()) { - builder.configFile(configFile); - } - return builder.build(); - } - private static void runVRT(String filePath) { - VisualRegressionTracker visualRegressionTracker = new VisualRegressionTracker(vrtConfig()); + VisualRegressionTracker visualRegressionTracker = new VisualRegressionTracker(); Map results = new HashMap<>(); results.put("allImageVerified", true); results.put("allPassed", true); @@ -50,14 +41,11 @@ private static void runVRT(String filePath) { System.exit(1); } visualRegressionTracker.start(); - //Use the default options builder or custom options builder. TestRunOptions.TestRunOptionsBuilder testRunOptionsBuilder = TestRunOptions.builder(); - //TestRunOptions.TestRunOptionsBuilder testRunOptionsBuilder = getTestRunOptionsBuilder(); File[] fileList = file.listFiles(); for (File eachFile : fileList) { String[] imageExtensions = {"png", "pdf"}; if (Arrays.asList(imageExtensions).contains(FilenameUtils.getExtension(eachFile.getName()))) { - //If the folder has pdf files, they will be converted to png to be able to compare. if (FilenameUtils.getExtension(eachFile.getName()).equalsIgnoreCase("pdf")) { List bufferedImageList = getPDFPagesAsImages(eachFile); for (int index = 0; index < bufferedImageList.size(); index++) { @@ -127,42 +115,11 @@ private static List getPDFPagesAsImages(File eachFile) throws Exc for (int pageNumber = 0; pageNumber < pdDocument.getNumberOfPages(); ++pageNumber) { BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(pageNumber, 300); bufferedImageList.add(bufferedImage); - //ImageIO.write(bufferedImage, "PNG", new File(eachFile.getPath().replace(".pdf", "_Page") + (pageNumber + 1) + ".png")); } } return bufferedImageList; } - private static TestRunOptions.TestRunOptionsBuilder getTestRunOptionsBuilder() { - //Use only the options you need in below line. - String maskData_x = "0"; - String maskData_y = "0"; - String maskData_width = "200"; - String maskData_height = "20"; - String browser = ""; - String device = "Sample"; - String os = ""; - String viewport = ""; - - TestRunOptions.TestRunOptionsBuilder testRunOptionsBuilder = TestRunOptions.builder() - .diffTollerancePercent(0.0f) - .os(os) - .browser(browser) - .device(device) - .viewport(viewport); - if (maskData_height != null && maskData_width != null) { - testRunOptionsBuilder.ignoreAreas(Collections.singletonList( - IgnoreAreas.builder() - .x(Long.valueOf(maskData_x)) - .y(Long.valueOf(maskData_y)) - .width(Long.valueOf(maskData_width)) - .height(Long.valueOf(maskData_height)) - .build() - )); - } - return testRunOptionsBuilder; - } - public static void writeHTMLFile(int passed, int failed, int unresolved) throws IOException { String content = "\n" + " \n" + From e4631f73ea0f63d53b4e9cbbcec3284e1719af19 Mon Sep 17 00:00:00 2001 From: Pavlo Strunkin Date: Mon, 2 Nov 2026 22:57:58 +0200 Subject: [PATCH 3/3] fix(standalone): print usage instead of misreading old-style arguments Co-Authored-By: Claude Opus 5.5 --- standalone-from-folder-maven/README.md | 2 +- .../src/main/java/CompareFromFile.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/standalone-from-folder-maven/README.md b/standalone-from-folder-maven/README.md index 719e438..03a143e 100644 --- a/standalone-from-folder-maven/README.md +++ b/standalone-from-folder-maven/README.md @@ -20,7 +20,7 @@ CI only compiles this example: it needs a folder of images. java -jar target/vrt-standalone-client-1.0.1.jar ``` -Without ``, the client reads the current folder. You can also run `CompareFromFile` from your IDE. +Without ``, the client reads the current folder; `help` prints the usage. You can also run `CompareFromFile` from your IDE. ## What to expect diff --git a/standalone-from-folder-maven/src/main/java/CompareFromFile.java b/standalone-from-folder-maven/src/main/java/CompareFromFile.java index 8f69c47..059a38e 100644 --- a/standalone-from-folder-maven/src/main/java/CompareFromFile.java +++ b/standalone-from-folder-maven/src/main/java/CompareFromFile.java @@ -24,6 +24,12 @@ public class CompareFromFile { static final String BOLD_TEXT = "\033[0;1m"; public static void main(String[] args) { + if (args.length > 1 || (args.length == 1 && args[0].equals("help"))) { + System.out.println("Usage: java -jar vrt-standalone-client.jar [folder]" + System.lineSeparator() + + "Tracks the PNG and PDF files in the folder (default: the current one)." + System.lineSeparator() + + "The connection comes from vrt.json in the current folder or VRT_* environment variables."); + System.exit(args.length > 1 ? 1 : 0); + } runVRT(args.length > 0 ? args[0] : "."); }