├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle ├── gradle ├── compilation.gradle ├── dependencies.gradle ├── deploy.gradle ├── tests.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── java │ └── org │ │ └── selenide │ │ └── selenoid │ │ ├── DownloadFileInSelenoid.java │ │ ├── SelenoidClient.java │ │ ├── SelenoidClipboard.java │ │ ├── SelenoidClipboardService.java │ │ └── SelenoidDownloadsFolder.java └── resources │ └── META-INF │ └── services │ ├── com.codeborne.selenide.ClipboardService │ └── com.codeborne.selenide.impl.DownloadFileToFolder └── test ├── java ├── integration │ ├── FileDownloadTest.java │ ├── RemoteWebdriverTest.java │ ├── SelenoidClipboardTest.java │ └── SelenoidSetup.java └── org │ └── selenide │ └── selenoid │ └── SelenoidClientTest.java └── resources └── simplelogger.properties /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 20 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: daily 12 | open-pull-requests-limit: 20 13 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | 9 | jobs: 10 | run-tests-job: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | - name: Set up JDK 16 | uses: actions/setup-java@v3 17 | with: 18 | distribution: 'temurin' 19 | cache: 'gradle' 20 | java-version: '11' 21 | - name: Build 22 | uses: gradle/gradle-build-action@v2 23 | with: 24 | arguments: clean build -x test 25 | - name: Run tests 26 | uses: gradle/gradle-build-action@v2 27 | with: 28 | arguments: check --info 29 | - uses: actions/upload-artifact@v3 30 | if: failure() 31 | with: 32 | name: test-report 33 | path: build/reports/ 34 | run-integrationTest-job: 35 | runs-on: ubuntu-latest 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@v3 39 | - uses: actions/setup-java@v3 40 | with: 41 | distribution: 'temurin' 42 | cache: 'gradle' 43 | java-version: '11' 44 | - name: Start selenoid 45 | uses: Xotabu4/selenoid-github-action@v2 46 | - name: Run integration tests 47 | uses: gradle/gradle-build-action@v2 48 | with: 49 | arguments: integrationTest --info --rerun-tasks 50 | - uses: actions/upload-artifact@v3 51 | if: failure() 52 | with: 53 | name: test-report 54 | path: build/reports/ 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | out 4 | build 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 2.3.8 (released 29.05.2023) 4 | Project "selenide-selenoid" was merged into project "selenide". 5 | Note that "groupId" will change from "org.selenide" to "com.codeborne". 6 | Now you will need to use dependency "com.codeborne:selenide-selenoid:6.15.0" or higher. 7 | 8 | ## 2.3.7 (released 15.05.2023) 9 | * Make the method $.download() wait for the full completion of download #19 10 | * #24 #26 bump Selenide from 6.12.4 to 6.14.1 11 | 12 | ## 2.3.6 (released 22.03.2023) 13 | * bump Selenide from 6.11.1 to 6.12.4 14 | * bump slf4j from 2.0.6 to 2.0.7 15 | 16 | ## 2.3.5 (released 23.01.2023) 17 | * declare Selenide dependency with `api` scope 18 | * upgrade to Selenide 6.11.1 19 | * update dependencies 20 | 21 | ## 2.3.4 (released 03.01.2023) 22 | * upgrade to Selenide 6.11.0 23 | 24 | ## 2.3.3 (released 08.12.2022) 25 | * upgrade to Selenide 6.10.2 26 | 27 | ## 2.3.2 28 | * upgrade to Selenide 6.10.0 29 | 30 | ## 2.3.1 31 | * upgrade to Selenide 6.9.0 32 | 33 | ## 2.3.0 (released 27.09.2022) 34 | * upgrade to Selenide 6.8.0 35 | 36 | ## 2.2.4 (released 14.08.2022) 37 | * upgrade to Selenide 6.7.2 38 | * #20 fix reading multiline clipboard content 39 | 40 | ## 2.2.3 (released 20.05.2022) 41 | * upgrade to Selenide 6.5.0 42 | 43 | ## 2.2.2 (released 16.05.2022) 44 | * #13 Now selenide-selenoid doesn't fetch `selenide-proxy` as transitive dependency, only `selenide`. 45 | * upgrade to Selenide 6.4.0 46 | 47 | ## 2.2.1 (released 16.02.2022) 48 | * upgrade to Selenide 6.3.2 49 | 50 | ## 2.2.0 (released 07.02.2022) 51 | * upgrade to Selenide 6.3.0 52 | * upgrade to Selenium webdriver 4.1.2 53 | 54 | ## 2.1.0 (released 24.11.2021) 55 | * upgrade to Selenide 6.1.0 56 | * upgrade to Selenium webdriver 4.1.0 57 | 58 | ## 2.0.0 (released 25.10.2021) 59 | * upgrade to Selenide 6.0.0 60 | * upgrade to Selenium webdriver 4.0.0 61 | 62 | ## 1.2.0 (released 28.09.2021) 63 | * upgrade to Selenide 5.25.0 64 | 65 | ## 1.1.7 (released 13.09.2021) 66 | * add Selenide and other dependencies 67 | 68 | ## 1.1.6 (released 13.09.2021) 69 | * upgrade to Selenide 5.24.3 70 | 71 | ## 1.1.5 (released 29.08.2021) 72 | * upgrade to Selenide 5.24.0 73 | 74 | ## 1.1.4 (released 17.07.2021) 75 | * upgrade to Selenide 5.23.0 76 | 77 | ## 1.1.3 (released 08.06.2021) 78 | * #10 fix ClassCastException when using EventFiringDriver 79 | * #10 upgrade to Selenide 5.22.0 80 | 81 | ## 1.1.2 (released 11.05.2021) 82 | * #9 add support for BasicAuth when downloading files from Selenoid 83 | 84 | ## 1.1.1 (released 23.04.2021) 85 | * upgrade to Selenide 5.20.4 86 | * upgrade to Gradle 7.0 87 | 88 | ## 1.1.0 (released 20.03.2021) 89 | * #6 Added get & set clipboard methods -- thanks to Dmitriy Budim 90 | * upgrade to Selenide 5.20.0 91 | 92 | ## 1.0.1 (released 29.01.2021) 93 | * #4 Support local browsers too 94 | * upgrade to Selenide 5.18.0 95 | 96 | ## 1.0.0 (released 20.11.2020) 97 | * initial version 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Andrei Solntsev, selenide.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Selenide Selenoid plugin 2 | ================================ 3 | 4 | A [Selenide](https://selenide.org) extension for working with Selenoid 5 | 6 | NB! Project "selenide-selenoid" was merged into project "selenide". 7 | Note that "groupId" will change from "org.selenide" to "com.codeborne". 8 | Now you will need to use dependency "com.codeborne:selenide-selenoid:6.15.0" or higher. 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'idea' 4 | } 5 | 6 | group = 'org.selenide' 7 | archivesBaseName = 'selenide-selenoid' 8 | version = '2.3.8' 9 | 10 | apply from: 'gradle/compilation.gradle' 11 | apply from: 'gradle/dependencies.gradle' 12 | apply from: 'gradle/tests.gradle' 13 | apply from: 'gradle/deploy.gradle' 14 | 15 | defaultTasks 'check' 16 | -------------------------------------------------------------------------------- /gradle/compilation.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | encoding = 'UTF-8' 3 | } 4 | 5 | tasks.withType(JavaCompile) { 6 | options.encoding = 'UTF-8' 7 | options.debug = true 8 | } 9 | compileJava.options.debugOptions.debugLevel = "source,lines,vars" 10 | 11 | sourceCompatibility = '1.8' 12 | targetCompatibility = '1.8' -------------------------------------------------------------------------------- /gradle/dependencies.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.versions = [ 3 | selenide: '6.14.1', 4 | junit: '5.9.3', 5 | assertj: '3.24.2', 6 | slf4j: '2.0.7' 7 | ] 8 | } 9 | 10 | dependencies { 11 | api("com.codeborne:selenide:${versions.selenide}") 12 | testRuntimeOnly("com.codeborne:selenide-proxy:${versions.selenide}") 13 | testImplementation "org.seleniumhq.selenium:selenium-http-jdk-client:4.9.1" 14 | implementation('org.apache.httpcomponents.client5:httpclient5:5.2.1') 15 | implementation("commons-io:commons-io:2.12.0") 16 | testImplementation("org.junit.jupiter:junit-jupiter-engine:${versions.junit}") 17 | testImplementation("org.assertj:assertj-core:${versions.assertj}") 18 | testRuntimeOnly("org.slf4j:slf4j-simple:${versions.slf4j}") 19 | testRuntimeOnly("com.codeborne:selenide-proxy:${versions.selenide}") 20 | } 21 | 22 | repositories { 23 | mavenCentral() 24 | maven { 25 | url uri('https://oss.sonatype.org/content/repositories/snapshots/') 26 | mavenContent { 27 | snapshotsOnly() 28 | } 29 | } 30 | mavenLocal() 31 | } 32 | 33 | configurations.all { 34 | resolutionStrategy.cacheChangingModulesFor 0, 'seconds' 35 | } 36 | -------------------------------------------------------------------------------- /gradle/deploy.gradle: -------------------------------------------------------------------------------- 1 | jar { 2 | manifest { 3 | attributes( 4 | "Automatic-Module-Name": project.group + '.' + project.name, 5 | "Implementation-Title": project.group + '.' + project.name, 6 | "Implementation-Version": version, 7 | "Implementation-Vendor": "selenide.org") 8 | } 9 | } 10 | 11 | task sourcesJar(type: Jar, dependsOn: classes) { 12 | classifier = 'sources' 13 | from sourceSets.main.allSource 14 | } 15 | 16 | javadoc { 17 | failOnError = false 18 | source = sourceSets.main.allJava 19 | } 20 | 21 | task javadocJar(type: Jar, dependsOn: javadoc) { 22 | classifier = 'javadoc' 23 | from javadoc.destinationDir 24 | } 25 | 26 | if (project.hasProperty("signing.keyId")) { 27 | apply plugin: 'signing' 28 | apply plugin: 'maven-publish' 29 | 30 | signing { 31 | afterEvaluate { 32 | sign publishing.publications.mavenJava 33 | } 34 | } 35 | 36 | artifacts { 37 | archives jar 38 | archives sourcesJar 39 | archives javadocJar 40 | } 41 | 42 | publishing { 43 | repositories { 44 | maven { 45 | name 'Maven' 46 | url project.version.endsWith("-SNAPSHOT") ? 47 | 'https://oss.sonatype.org/content/repositories/snapshots/' : 48 | 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' 49 | credentials { 50 | username "$sonatypeUsername" 51 | password "$sonatypePassword" 52 | } 53 | } 54 | } 55 | publications { 56 | mavenJava(MavenPublication) { 57 | groupId "${project.group}" 58 | artifactId "${project.name}" 59 | 60 | artifact(jar) 61 | artifact(sourcesJar) 62 | artifact(javadocJar) 63 | 64 | pom { 65 | name = archivesBaseName 66 | description = 'Selenoid plugin for Selenide' 67 | url = 'https://github.com/selenide/selenide-selenoid' 68 | licenses { 69 | license { 70 | name = 'MIT' 71 | url = 'https://opensource.org/licenses/MIT' 72 | } 73 | } 74 | developers { 75 | developer { 76 | id = 'asolntsev' 77 | name = 'Andrei Solntsev' 78 | } 79 | } 80 | scm { 81 | connection = 'scm:git@github.com:selenide/selenide-selenoid.git' 82 | developerConnection = 'scm:git@github.com:selenide/selenide-selenoid.git' 83 | url = 'https://github.com/selenide/selenide-selenoid' 84 | } 85 | withXml { 86 | def dependenciesNode = asNode().appendNode('dependencies') 87 | 88 | configurations.api.allDependencies.each { 89 | exportDependency(dependenciesNode, it, null) 90 | } 91 | (configurations.implementation.allDependencies-configurations.api.allDependencies).each { 92 | exportDependency(dependenciesNode, it, 'runtime') 93 | } 94 | } 95 | distributionManagement { 96 | relocation { 97 | groupId = 'com.codeborne' 98 | artifactId = 'selenide-selenoid' 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | 107 | private static def exportDependency(dependenciesNode, dependency, scope) { 108 | def dependencyNode = dependenciesNode.appendNode('dependency') 109 | dependencyNode.appendNode('groupId', dependency.group) 110 | dependencyNode.appendNode('artifactId', dependency.name) 111 | dependencyNode.appendNode('version', dependency.version) 112 | if (scope) { 113 | dependencyNode.appendNode('scope', scope) 114 | } 115 | 116 | if (dependency.getExcludeRules()) { 117 | exclude(dependencyNode, dependency.getExcludeRules()) 118 | } 119 | } 120 | 121 | private static def exclude(dependencyNode, excludeRules) { 122 | def exclusionsNode = dependencyNode.appendNode('exclusions') 123 | excludeRules.each { 124 | def exclusionNode = exclusionsNode.appendNode('exclusion') 125 | exclusionNode.appendNode('groupId', it.group) 126 | exclusionNode.appendNode('artifactId', it.module ?: '*') 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /gradle/tests.gradle: -------------------------------------------------------------------------------- 1 | test { 2 | include 'org/selenide/selenoid/**/*' 3 | } 4 | 5 | tasks.register('test-chrome', Test) { 6 | systemProperties['selenide.browser'] = 'chrome' 7 | include 'integration/**/*' 8 | exclude 'org/selenide/selenoid/**/*' 9 | } 10 | 11 | tasks.withType(Test).configureEach { 12 | useJUnitPlatform() 13 | systemProperty('file.encoding', encoding) 14 | outputs.upToDateWhen { 15 | false 16 | } 17 | } 18 | 19 | task integrationTest(type: Test) { 20 | include 'integration/**/*' 21 | exclude 'org.selenide.selenoid/**/*' 22 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/selenide/selenide-selenoid/93da17a23663ff8eb84410c8a7aa6b5f0c6bfa4b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'selenide-selenoid' 2 | -------------------------------------------------------------------------------- /src/main/java/org/selenide/selenoid/DownloadFileInSelenoid.java: -------------------------------------------------------------------------------- 1 | package org.selenide.selenoid; 2 | 3 | import com.codeborne.selenide.DownloadsFolder; 4 | import com.codeborne.selenide.Driver; 5 | import com.codeborne.selenide.files.FileFilter; 6 | import com.codeborne.selenide.impl.Downloader; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import javax.annotation.Nonnull; 11 | import javax.annotation.Nullable; 12 | import javax.annotation.ParametersAreNonnullByDefault; 13 | import java.io.File; 14 | import java.io.FileNotFoundException; 15 | 16 | 17 | @ParametersAreNonnullByDefault 18 | public class DownloadFileInSelenoid extends com.codeborne.selenide.impl.DownloadFileToFolder { 19 | private static final Logger log = LoggerFactory.getLogger(DownloadFileInSelenoid.class); 20 | private final Downloader downloader; 21 | 22 | public DownloadFileInSelenoid() { 23 | this(new Downloader()); 24 | } 25 | 26 | DownloadFileInSelenoid(Downloader downloader) { 27 | this.downloader = downloader; 28 | } 29 | 30 | @Nullable 31 | protected DownloadsFolder getDownloadsFolder(Driver driver) { 32 | return isLocalBrowser(driver) ? 33 | super.getDownloadsFolder(driver) : 34 | new SelenoidDownloadsFolder(driver); 35 | } 36 | 37 | @Override 38 | protected void waitWhileFilesAreBeingModified(Driver driver, DownloadsFolder folder, long timeout, long pollingInterval) { 39 | if (isLocalBrowser(driver)) { 40 | super.waitWhileFilesAreBeingModified(driver, folder, timeout, pollingInterval); 41 | } 42 | else { 43 | // In Selenoid, we don't know files' modification time :( 44 | } 45 | } 46 | 47 | @Override 48 | protected void failFastIfNoChanges(Driver driver, DownloadsFolder folder, FileFilter filter, 49 | long start, long timeout, long incrementTimeout) throws FileNotFoundException { 50 | if (isLocalBrowser(driver)) { 51 | super.failFastIfNoChanges(driver, folder, filter, start, timeout, incrementTimeout); 52 | } 53 | else { 54 | // In Selenoid, we don't know files' modification time :( 55 | } 56 | } 57 | 58 | @Nonnull 59 | @Override 60 | protected File archiveFile(Driver driver, File downloadedFile) { 61 | if (isLocalBrowser(driver)) { 62 | return super.archiveFile(driver, downloadedFile); 63 | } 64 | SelenoidClient selenoidClient = new SelenoidClient(driver.config().remote(), driver.getSessionId().toString()); 65 | File uniqueFolder = downloader.prepareTargetFolder(driver.config()); 66 | File localFile = selenoidClient.download(downloadedFile.getName(), uniqueFolder); 67 | log.debug("Copied the downloaded file {} from Selenoid to {}", downloadedFile, localFile); 68 | return localFile; 69 | } 70 | 71 | private boolean isLocalBrowser(Driver driver) { 72 | return driver.config().remote() == null; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/selenide/selenoid/SelenoidClient.java: -------------------------------------------------------------------------------- 1 | package org.selenide.selenoid; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.reflect.TypeToken; 5 | import org.apache.commons.io.IOUtils; 6 | import org.apache.hc.core5.net.URIBuilder; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import javax.annotation.CheckReturnValue; 11 | import javax.annotation.Nonnull; 12 | import javax.annotation.ParametersAreNonnullByDefault; 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.OutputStream; 17 | import java.io.OutputStreamWriter; 18 | import java.lang.reflect.Type; 19 | import java.net.HttpURLConnection; 20 | import java.net.MalformedURLException; 21 | import java.net.URISyntaxException; 22 | import java.net.URL; 23 | import java.nio.file.Files; 24 | import java.util.Arrays; 25 | import java.util.Base64; 26 | import java.util.List; 27 | 28 | import static java.nio.charset.StandardCharsets.UTF_8; 29 | 30 | @ParametersAreNonnullByDefault 31 | public class SelenoidClient { 32 | private static final Logger log = LoggerFactory.getLogger(SelenoidClient.class); 33 | private static final Type listType = new TypeToken>() {}.getType(); 34 | private static final Gson gson = new Gson(); 35 | final String baseUrl; 36 | private final String sessionId; 37 | 38 | public SelenoidClient(String hubUrl, String sessionId) { 39 | if (!hubUrl.endsWith("/wd/hub")) { 40 | throw new IllegalArgumentException("Expect hub url to end with /wd/hub, but received: " + hubUrl); 41 | } 42 | this.baseUrl = hubUrl.replace("/wd/hub", ""); 43 | this.sessionId = sessionId; 44 | } 45 | 46 | @CheckReturnValue 47 | @Nonnull 48 | public List downloads() { 49 | URL url = url(baseUrl + "/download/" + sessionId + "/?json"); 50 | String fileNamesJson = readToString(url); 51 | List fileNames = gson.fromJson(fileNamesJson, listType); 52 | log.debug("Retrieved files from {}: {}", url, fileNames); 53 | return fileNames; 54 | } 55 | 56 | @CheckReturnValue 57 | @Nonnull 58 | public File download(String fileName, File targetFolder) { 59 | URL url = urlOfDownloadedFile(fileName); 60 | try (InputStream in = connectionFromUrl(url).getInputStream()) { 61 | File file = new File(targetFolder, fileName); 62 | try (OutputStream out = Files.newOutputStream(file.toPath())) { 63 | IOUtils.copyLarge(in, out); 64 | } 65 | log.debug("Downloaded file from {} to {}", url, file.getAbsolutePath()); 66 | return file; 67 | } 68 | catch (IOException e) { 69 | throw new RuntimeException("Failed to download file " + url, e); 70 | } 71 | } 72 | 73 | public void deleteDownloadedFiles() { 74 | log.debug("Deleting downloaded files..."); 75 | List downloadedFiles = downloads(); 76 | downloadedFiles.forEach(this::deleteDownloadedFile); 77 | log.debug("Deleted {} downloaded files", downloadedFiles.size()); 78 | } 79 | 80 | public void deleteDownloadedFile(String fileName) { 81 | URL url = urlOfDownloadedFile(fileName); 82 | 83 | try { 84 | HttpURLConnection connection = connectionFromUrl(url); 85 | connection.setRequestMethod("DELETE"); 86 | int responseCode = connection.getResponseCode(); 87 | if (responseCode != 200) { 88 | throw new RuntimeException("Failed to deleted downloaded file " + fileName + 89 | ", received http status " + responseCode); 90 | } 91 | log.debug("Deleted downloaded file {}", url); 92 | } 93 | catch (IOException e) { 94 | throw new RuntimeException("Failed to download file " + url, e); 95 | } 96 | } 97 | 98 | @CheckReturnValue 99 | @Nonnull 100 | public String getClipboardText() { 101 | try { 102 | HttpURLConnection connection = connectionFromUrl(url(baseUrl, "clipboard", sessionId)); 103 | int code = connection.getResponseCode(); 104 | if (code != 200) 105 | throw new RuntimeException("Something went wrong while getting clipboard! Response code: " + code); 106 | 107 | try (InputStream in = connection.getInputStream()) { 108 | return IOUtils.toString(in, UTF_8); 109 | } 110 | } catch (IOException e) { 111 | throw new RuntimeException("Something went wrong while getting clipboard!", e); 112 | } 113 | } 114 | 115 | public void setClipboardText(String text) { 116 | try { 117 | HttpURLConnection connection = connectionFromUrl(url(baseUrl, "clipboard", sessionId)); 118 | connection.setRequestMethod("POST"); 119 | connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 120 | connection.setConnectTimeout(10000); 121 | connection.setRequestMethod("POST"); 122 | connection.setDoOutput(true); 123 | try (OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream())) { 124 | writer.write(text); 125 | } 126 | int code = connection.getResponseCode(); 127 | if (code != 200) 128 | throw new RuntimeException("Something went wrong while writing clipboard! Response code: " + code); 129 | } catch (IOException e) { 130 | throw new RuntimeException("Can't set clipboard content! ", e); 131 | } 132 | } 133 | 134 | @CheckReturnValue 135 | @Nonnull 136 | URL urlOfDownloadedFile(String fileName) { 137 | return url(baseUrl, "download", sessionId, fileName); 138 | } 139 | 140 | @CheckReturnValue 141 | @Nonnull 142 | private URL url(String url) { 143 | try { 144 | return new URL(url); 145 | } 146 | catch (MalformedURLException e) { 147 | throw new RuntimeException("Failed to build valid URL from " + url, e); 148 | } 149 | } 150 | 151 | @CheckReturnValue 152 | @Nonnull 153 | private URL url(String base, String... pathSegments) { 154 | try { 155 | return new URIBuilder(base) 156 | .setPathSegments(pathSegments) 157 | .build().toURL(); 158 | } 159 | catch (URISyntaxException | MalformedURLException e) { 160 | throw new RuntimeException("Failed to build valid URL from " + base + '+' + Arrays.toString(pathSegments), e); 161 | } 162 | } 163 | 164 | @CheckReturnValue 165 | @Nonnull 166 | private String readToString(URL url) { 167 | try (InputStream in = connectionFromUrl(url).getInputStream()) { 168 | return IOUtils.toString(in, UTF_8); 169 | } 170 | catch (IOException e) { 171 | throw new RuntimeException("Failed to fetch data from " + url, e); 172 | } 173 | } 174 | 175 | @CheckReturnValue 176 | @Nonnull 177 | private HttpURLConnection connectionFromUrl(URL url) throws IOException { 178 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 179 | if (url.getUserInfo() != null) { 180 | String basicAuth = "Basic " + new String(Base64.getEncoder().encode(url.getUserInfo().getBytes())); 181 | connection.setRequestProperty("Authorization", basicAuth); 182 | } 183 | return connection; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/org/selenide/selenoid/SelenoidClipboard.java: -------------------------------------------------------------------------------- 1 | package org.selenide.selenoid; 2 | 3 | import com.codeborne.selenide.Clipboard; 4 | import com.codeborne.selenide.DefaultClipboard; 5 | import com.codeborne.selenide.Driver; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import javax.annotation.CheckReturnValue; 10 | import javax.annotation.Nonnull; 11 | import javax.annotation.ParametersAreNonnullByDefault; 12 | 13 | @ParametersAreNonnullByDefault 14 | public class SelenoidClipboard implements Clipboard { 15 | private static final Logger log = LoggerFactory.getLogger(SelenoidClipboard.class); 16 | 17 | private final Driver driver; 18 | 19 | SelenoidClipboard(Driver driver) { 20 | this.driver = driver; 21 | } 22 | 23 | @Nonnull 24 | @CheckReturnValue 25 | @Override 26 | public Driver driver() { 27 | return driver; 28 | } 29 | 30 | @Nonnull 31 | @CheckReturnValue 32 | @Override 33 | public Clipboard object() { 34 | return this; 35 | } 36 | 37 | @CheckReturnValue 38 | @Nonnull 39 | @Override 40 | public String getText() { 41 | if (driver.config().remote() == null) { 42 | log.debug("Working in local browser. Switching to a default Clipboard implementation."); 43 | return new DefaultClipboard(driver).getText(); 44 | } 45 | else { 46 | return new SelenoidClient(driver.config().remote(), driver.getSessionId().toString()).getClipboardText(); 47 | } 48 | } 49 | 50 | @Override 51 | public void setText(String text) { 52 | if (driver.config().remote() == null) { 53 | log.debug("Working in local browser. Switching to a default Clipboard implementation."); 54 | new DefaultClipboard(driver).setText(text); 55 | } 56 | else { 57 | new SelenoidClient(driver.config().remote(), driver.getSessionId().toString()).setClipboardText(text); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/selenide/selenoid/SelenoidClipboardService.java: -------------------------------------------------------------------------------- 1 | package org.selenide.selenoid; 2 | 3 | import com.codeborne.selenide.Clipboard; 4 | import com.codeborne.selenide.ClipboardService; 5 | import com.codeborne.selenide.Driver; 6 | 7 | import javax.annotation.CheckReturnValue; 8 | import javax.annotation.Nonnull; 9 | import javax.annotation.ParametersAreNonnullByDefault; 10 | 11 | @ParametersAreNonnullByDefault 12 | public class SelenoidClipboardService extends ClipboardService { 13 | @Nonnull 14 | @CheckReturnValue 15 | @Override 16 | public Clipboard getClipboard(Driver driver) { 17 | return new SelenoidClipboard(driver); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/selenide/selenoid/SelenoidDownloadsFolder.java: -------------------------------------------------------------------------------- 1 | package org.selenide.selenoid; 2 | 3 | import com.codeborne.selenide.DownloadsFolder; 4 | import com.codeborne.selenide.Driver; 5 | import com.codeborne.selenide.files.DownloadedFile; 6 | 7 | import javax.annotation.CheckReturnValue; 8 | import javax.annotation.Nonnull; 9 | import java.io.File; 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | import static java.util.Collections.emptyMap; 14 | import static java.util.stream.Collectors.toList; 15 | 16 | public class SelenoidDownloadsFolder implements DownloadsFolder { 17 | private final SelenoidClient selenoidClient; 18 | 19 | public SelenoidDownloadsFolder(Driver driver) { 20 | this.selenoidClient = new SelenoidClient(driver.config().remote(), driver.getSessionId().toString()); 21 | } 22 | 23 | @Override 24 | public void cleanupBeforeDownload() { 25 | selenoidClient.deleteDownloadedFiles(); 26 | } 27 | 28 | @Override 29 | public void deleteIfEmpty() { 30 | } 31 | 32 | @Nonnull 33 | @CheckReturnValue 34 | @Override 35 | public List files() { 36 | List files = selenoidClient.downloads(); 37 | return files.stream().map(name -> new File(name)).collect(Collectors.toList()); 38 | } 39 | 40 | @Nonnull 41 | @CheckReturnValue 42 | @Override 43 | public List filesNewerThan(long modifiedAfterTs) { 44 | return files().stream() 45 | .map(file -> new DownloadedFile(file, emptyMap())) 46 | .collect(toList()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/com.codeborne.selenide.ClipboardService: -------------------------------------------------------------------------------- 1 | org.selenide.selenoid.SelenoidClipboardService -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/com.codeborne.selenide.impl.DownloadFileToFolder: -------------------------------------------------------------------------------- 1 | org.selenide.selenoid.DownloadFileInSelenoid -------------------------------------------------------------------------------- /src/test/java/integration/FileDownloadTest.java: -------------------------------------------------------------------------------- 1 | package integration; 2 | 3 | import com.codeborne.selenide.Configuration; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | 10 | import static com.codeborne.selenide.DownloadOptions.using; 11 | import static com.codeborne.selenide.FileDownloadMode.FOLDER; 12 | import static com.codeborne.selenide.FileDownloadMode.HTTPGET; 13 | import static com.codeborne.selenide.FileDownloadMode.PROXY; 14 | import static com.codeborne.selenide.Selenide.$; 15 | import static com.codeborne.selenide.Selenide.open; 16 | import static com.codeborne.selenide.files.FileFilters.withExtension; 17 | import static integration.SelenoidSetup.checkDownload; 18 | import static integration.SelenoidSetup.resetSelenoidSettings; 19 | import static org.apache.commons.lang3.StringUtils.rightPad; 20 | import static org.assertj.core.api.Assertions.assertThat; 21 | 22 | @ExtendWith(SelenoidSetup.class) 23 | public class FileDownloadTest { 24 | @Test 25 | void downloadFileInSelenoid_using_httpGet() throws IOException { 26 | Configuration.proxyEnabled = false; 27 | Configuration.fileDownload = HTTPGET; 28 | checkDownload(); 29 | } 30 | 31 | @Test 32 | void downloadFileInSelenoid_using_folder() throws IOException { 33 | Configuration.proxyEnabled = false; 34 | Configuration.fileDownload = FOLDER; 35 | checkDownload(); 36 | } 37 | 38 | @Test 39 | void downloadFileInSelenoid_using_proxy() throws IOException { 40 | Configuration.proxyEnabled = true; 41 | Configuration.fileDownload = PROXY; 42 | checkDownload(); 43 | } 44 | 45 | @Test 46 | void slowDownloadToFolder() throws IOException { 47 | String fileContent = rightPad("Lorem ipsum dolor sit amet", 4096, "\nlaborum"); 48 | open("https://selenide.org/test-page/download.html"); 49 | $("[name=delay]").setValue("3000"); 50 | $("#lore-ipsum").setValue(fileContent); 51 | 52 | File file = $("#slow-download").download(using(FOLDER).withFilter(withExtension("txt"))); 53 | 54 | assertThat(file).hasName("hello.txt"); 55 | assertThat(file).content().isEqualToIgnoringWhitespace(fileContent); 56 | } 57 | 58 | @Test 59 | void downloadFileInLocalBrowser() throws IOException { 60 | resetSelenoidSettings(); 61 | Configuration.headless = true; 62 | Configuration.fileDownload = FOLDER; 63 | checkDownload(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/integration/RemoteWebdriverTest.java: -------------------------------------------------------------------------------- 1 | package integration; 2 | 3 | import com.codeborne.selenide.Configuration; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import org.openqa.selenium.remote.RemoteWebDriver; 8 | 9 | import java.io.IOException; 10 | import java.net.URL; 11 | 12 | import static com.codeborne.selenide.FileDownloadMode.HTTPGET; 13 | import static com.codeborne.selenide.WebDriverRunner.setWebDriver; 14 | import static integration.SelenoidSetup.capabilities; 15 | import static integration.SelenoidSetup.checkDownload; 16 | import static integration.SelenoidSetup.selenoidUrl; 17 | 18 | @ExtendWith(SelenoidSetup.class) 19 | public class RemoteWebdriverTest { 20 | @BeforeEach 21 | void setUp() { 22 | Configuration.proxyEnabled = false; 23 | Configuration.fileDownload = HTTPGET; 24 | } 25 | 26 | @Test 27 | void canStartRemoteWebDriver() throws IOException { 28 | RemoteWebDriver driver = new RemoteWebDriver(new URL(selenoidUrl()), capabilities(), false); 29 | setWebDriver(driver); 30 | checkDownload(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/integration/SelenoidClipboardTest.java: -------------------------------------------------------------------------------- 1 | package integration; 2 | 3 | import com.codeborne.selenide.Configuration; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | 8 | import static com.codeborne.selenide.ClipboardConditions.content; 9 | import static com.codeborne.selenide.Condition.attribute; 10 | import static com.codeborne.selenide.Condition.visible; 11 | import static com.codeborne.selenide.FileDownloadMode.HTTPGET; 12 | import static com.codeborne.selenide.Selenide.$; 13 | import static com.codeborne.selenide.Selenide.clipboard; 14 | import static com.codeborne.selenide.Selenide.executeJavaScript; 15 | import static com.codeborne.selenide.Selenide.open; 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | 18 | @ExtendWith(SelenoidSetup.class) 19 | public class SelenoidClipboardTest { 20 | 21 | @BeforeEach 22 | public void prepare() { 23 | Configuration.proxyEnabled = false; 24 | Configuration.fileDownload = HTTPGET; 25 | 26 | open("/clipboard.html"); 27 | } 28 | 29 | @Test 30 | public void getClipboardContent() { 31 | $("#text-input").shouldHave(attribute("value", "Hello World")); 32 | $("#copy-button").shouldBe(visible).click(); 33 | clipboard().shouldHave(content("Hello World")); 34 | assertEquals("Hello World", clipboard().getText()); 35 | } 36 | 37 | @Test 38 | public void setClipboardContent() { 39 | clipboard().setText("John Wick"); 40 | assertEquals("John Wick", clipboard().getText()); 41 | clipboard().shouldHave(content("John Wick")); 42 | } 43 | 44 | @Test 45 | public void setAndGetClipboardMultilineContent() { 46 | String multilineText = "John\nWick\r\nThe\nGreat\r"; 47 | clipboard().setText(multilineText); 48 | assertEquals(multilineText, clipboard().getText()); 49 | clipboard().shouldHave(content(multilineText)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/integration/SelenoidSetup.java: -------------------------------------------------------------------------------- 1 | package integration; 2 | 3 | import com.codeborne.selenide.Configuration; 4 | import com.codeborne.selenide.FileDownloadMode; 5 | import com.google.common.collect.ImmutableMap; 6 | import org.junit.jupiter.api.extension.AfterEachCallback; 7 | import org.junit.jupiter.api.extension.BeforeEachCallback; 8 | import org.junit.jupiter.api.extension.ExtensionContext; 9 | import org.openqa.selenium.remote.DesiredCapabilities; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | 14 | import static com.codeborne.selenide.Selectors.byText; 15 | import static com.codeborne.selenide.Selenide.$; 16 | import static com.codeborne.selenide.Selenide.closeWebDriver; 17 | import static com.codeborne.selenide.Selenide.open; 18 | import static com.codeborne.selenide.files.FileFilters.withExtension; 19 | import static java.nio.charset.StandardCharsets.UTF_8; 20 | import static org.apache.commons.io.FileUtils.readFileToString; 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | 23 | public class SelenoidSetup implements BeforeEachCallback, AfterEachCallback { 24 | @Override 25 | public void beforeEach(final ExtensionContext context) { 26 | closeWebDriver(); 27 | System.setProperty("webdriver.http.factory", "jdk-http-client"); 28 | Configuration.baseUrl = "https://selenide.org/test-page"; 29 | Configuration.browserCapabilities = capabilities(); 30 | Configuration.browser = "chrome"; 31 | Configuration.remote = selenoidUrl(); 32 | Configuration.headless = false; 33 | Configuration.fileDownload = FileDownloadMode.HTTPGET; 34 | Configuration.proxyEnabled = false; 35 | } 36 | 37 | static String selenoidUrl() { 38 | return "http://localhost:4444/wd/hub"; 39 | } 40 | 41 | static void resetSelenoidSettings() { 42 | Configuration.remote = null; 43 | Configuration.browserCapabilities = new DesiredCapabilities(); 44 | } 45 | 46 | @Override 47 | public void afterEach(final ExtensionContext context) { 48 | closeWebDriver(); 49 | resetSelenoidSettings(); 50 | } 51 | 52 | static DesiredCapabilities capabilities() { 53 | DesiredCapabilities capabilities = new DesiredCapabilities(); 54 | capabilities.setBrowserName("chrome"); 55 | capabilities.setCapability("selenoid:options", ImmutableMap.of( 56 | "enableVNC", true, 57 | "enableVideo", true 58 | )); 59 | return capabilities; 60 | } 61 | 62 | static void checkDownload() throws IOException { 63 | open("/download.html"); 64 | File file = $(byText("hello-world.txt")).download(withExtension("txt")); 65 | assertThat(file).hasName("hello-world.txt"); 66 | assertThat(readFileToString(file, UTF_8)).isEqualTo("Hello, world!"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/org/selenide/selenoid/SelenoidClientTest.java: -------------------------------------------------------------------------------- 1 | package org.selenide.selenoid; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.net.MalformedURLException; 6 | import java.net.URL; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 10 | 11 | class SelenoidClientTest { 12 | @Test 13 | void extractsSelenoidBaseUrlFromHubUrl() { 14 | SelenoidClient client = new SelenoidClient("http://localhost:4444/wd/hub", "sid-01"); 15 | assertThat(client.baseUrl).isEqualTo("http://localhost:4444"); 16 | } 17 | 18 | @Test 19 | void hubsUrlShouldEndWithWdHub() { 20 | assertThatThrownBy(() -> new SelenoidClient("http://localhost:4444", "sid-01")) 21 | .isInstanceOf(IllegalArgumentException.class) 22 | .hasMessage("Expect hub url to end with /wd/hub, but received: http://localhost:4444"); 23 | } 24 | 25 | @Test 26 | void encodesFileNameInUrl() throws MalformedURLException { 27 | SelenoidClient client = new SelenoidClient("http://localhost:4444/wd/hub", "sid-01"); 28 | assertThat(client.urlOfDownloadedFile("some-file.txt")).isEqualTo( 29 | new URL("http://localhost:4444/download/sid-01/some-file.txt") 30 | ); 31 | assertThat(client.urlOfDownloadedFile("some file (2).txt")).isEqualTo( 32 | new URL("http://localhost:4444/download/sid-01/some%20file%20%282%29.txt") 33 | ); 34 | } 35 | } -------------------------------------------------------------------------------- /src/test/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.defaultLogLevel=debug 2 | org.slf4j.simpleLogger.showShortLogName=true 3 | org.slf4j.simpleLogger.showDateTime=true 4 | org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss:SSS 5 | --------------------------------------------------------------------------------