diff --git a/README.md b/README.md index 2f3841a..b2c0bc4 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ This repository contains the source code for BrowserStack's Gradle plugin. +**Compatibility:** Android Gradle Plugin (AGP) 9.x, Gradle 9.1+. The plugin uses the `androidComponents.onVariants()` API required by AGP 9. For AGP 2.x–6.x, use plugin version 3.1.6 or earlier. + ## PURPOSE diff --git a/build.gradle b/build.gradle index d00ceff..e4a9cd5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ plugins { id 'java-gradle-plugin' - id 'com.gradle.plugin-publish' version '0.11.0' + id 'com.gradle.plugin-publish' version '2.1.0' id 'maven-publish' } version '3.1.6' group 'com.browserstack' -pluginBundle { +gradlePlugin { website = 'https://www.browserstack.com' vcsUrl = 'https://github.com/browserstack/browserstack-gradle-plugin' @@ -17,31 +17,32 @@ pluginBundle { id = 'com.browserstack.gradle' displayName = 'BrowserStack\'s Gradle Plugin' description = 'Runs Espresso tests on BrowserStack' - tags = ['espresso', 'test', 'browserstack', 'app', 'automate', - 'app-automate', 'appautomate', 'app-live', 'applive'] - version = '3.1.6' - group = 'com.browserstack' + tags.set(['espresso', 'test', 'browserstack', 'app', 'automate', + 'app-automate', 'appautomate', 'app-live', 'applive']) + implementationClass = 'com.browserstack.gradle.BrowserStackPlugin' } browserstackSDKPlugin { id = 'com.browserstack.gradle-sdk' displayName = 'BrowserStack SDK Gradle Solution' description = 'Cross browser testing for Gradle based projects with BrowserStack SDK' - tags = ['test', 'browserstack'] - version = '3.1.6' - group = 'com.browserstack' + tags.set(['test', 'browserstack']) + implementationClass = 'com.browserstack.gradle.BrowserStackSDKPlugin' } } } +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + repositories { maven { url 'https://maven.google.com' } - // google() - jcenter() mavenCentral() } dependencies { - // https://mvnrepository.com/artifact/com.android.tools.build/gradle - implementation group: 'com.android.tools.build', name: 'gradle', version: '2.3.0' + implementation group: 'com.android.tools.build', name: 'gradle', version: '9.0.1' implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1' + compileOnly group: 'org.jetbrains', name: 'annotations', version: '26.0.2' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180..d997cfc 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bf3de21..dbc3ce4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787..0262dcb 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# 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"' +# 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 @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -133,22 +132,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + 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 @@ -165,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -193,18 +198,27 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# 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" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -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. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f..e509b2d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @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 +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +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. 1>&2 goto fail @@ -56,32 +59,33 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +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. 1>&2 goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/src/main/java/com/browserstack/gradle/AppAutomateUploadTask.java b/src/main/java/com/browserstack/gradle/AppAutomateUploadTask.java index 8d653ef..a2528f1 100644 --- a/src/main/java/com/browserstack/gradle/AppAutomateUploadTask.java +++ b/src/main/java/com/browserstack/gradle/AppAutomateUploadTask.java @@ -6,6 +6,9 @@ import java.nio.file.Path; import java.util.Map; +/** + * Gradle task that uploads the debug APK to BrowserStack App Automate and prints the test URL. + */ public class AppAutomateUploadTask extends BrowserStackTask { private void displayTestURL(String app_url) { @@ -13,6 +16,7 @@ private void displayTestURL(String app_url) { System.out.println("Start testing at " + Constants.APP_AUTOMATE_HOST + "/#app_hashed_id=" + app_hashed_id); } + /** Ensures username and accessKey are set (from extension or env). */ public void verifyParams() throws Exception { String username = this.getUsername(); String accessKey = this.getAccessKey(); @@ -21,6 +25,7 @@ public void verifyParams() throws Exception { } } + /** Uploads the debug APK to App Automate and prints the session URL. */ @TaskAction void upload() throws Exception { verifyParams(); diff --git a/src/main/java/com/browserstack/gradle/AppLiveUploadTask.java b/src/main/java/com/browserstack/gradle/AppLiveUploadTask.java index 9336706..fab9603 100644 --- a/src/main/java/com/browserstack/gradle/AppLiveUploadTask.java +++ b/src/main/java/com/browserstack/gradle/AppLiveUploadTask.java @@ -5,6 +5,9 @@ import java.util.Map; import java.nio.file.Path; +/** + * Gradle task that uploads the debug APK to BrowserStack App Live and prints the test URL. + */ public class AppLiveUploadTask extends BrowserStackTask { private void displayTestURL(String app_url) { @@ -12,6 +15,7 @@ private void displayTestURL(String app_url) { System.out.println("Start testing at " + Constants.APP_LIVE_HOST + "/#app_hashed_id=" + app_hashed_id); } + /** Ensures username and accessKey are set (from extension or env). */ public void verifyParams() throws Exception { String username = this.getUsername(); String accessKey = this.getAccessKey(); @@ -20,6 +24,7 @@ public void verifyParams() throws Exception { } } + /** Uploads the debug APK to App Live and prints the session URL. */ @TaskAction void uploadAndExecuteTest() throws Exception { verifyParams(); diff --git a/src/main/java/com/browserstack/gradle/BrowserStackConfigExtension.java b/src/main/java/com/browserstack/gradle/BrowserStackConfigExtension.java index eafa29b..47f1186 100644 --- a/src/main/java/com/browserstack/gradle/BrowserStackConfigExtension.java +++ b/src/main/java/com/browserstack/gradle/BrowserStackConfigExtension.java @@ -2,7 +2,11 @@ import java.util.HashMap; -// This class is for getting browserstack configuration from gradle file. +/** + * Gradle extension for BrowserStack credentials and options. + * Configure via the {@code browserStackConfig { ... }} block in build.gradle. + * Username and accessKey default to BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY env vars. + */ public class BrowserStackConfigExtension { private String username = System.getenv("BROWSERSTACK_USERNAME"); @@ -11,9 +15,7 @@ public class BrowserStackConfigExtension { private String configFilePath; private String customId; - /** - * Enables debugging with more verbose logs - */ + /** When true, enables debugging with more verbose logs. */ private boolean isDebug = false; public String getUsername() { diff --git a/src/main/java/com/browserstack/gradle/BrowserStackPlugin.java b/src/main/java/com/browserstack/gradle/BrowserStackPlugin.java index 064694f..b8ae555 100644 --- a/src/main/java/com/browserstack/gradle/BrowserStackPlugin.java +++ b/src/main/java/com/browserstack/gradle/BrowserStackPlugin.java @@ -1,118 +1,132 @@ package com.browserstack.gradle; -import com.android.build.gradle.AppExtension; -import com.android.build.gradle.api.ApplicationVariant; -import org.gradle.api.DomainObjectSet; +import com.android.build.api.variant.ApplicationAndroidComponentsExtension; +import com.android.build.api.variant.ApplicationVariant; +import org.gradle.api.Action; import org.gradle.api.Plugin; import org.gradle.api.Project; -import java.util.ArrayList; -import java.util.List; - +/** + * Gradle plugin that registers BrowserStack tasks for each Android application variant. + * Creates execute*TestsOnBrowserstack, upload*ToBrowserstackAppLive, upload*ToBrowserstackAppAutomate, + * and browserstackCLIWrapper tasks. Requires the Android Application plugin to be applied first. + */ public class BrowserStackPlugin implements Plugin { private static final String DEFAULT_GROUP = "BrowserStack"; + /** + * Applies the plugin: creates browserStackConfig extension and per-variant BrowserStack tasks. + * + * @param project the Gradle project + */ public void apply(Project project) { BrowserStackConfigExtension browserStackConfigExtension = project.getExtensions() .create("browserStackConfig", BrowserStackConfigExtension.class); - // Get android appExtension - AppExtension appExtension = (AppExtension) project.getExtensions().getByName("android"); - - // Get all application variants or flavour combinations. - DomainObjectSet appVariants = appExtension.getApplicationVariants(); + ApplicationAndroidComponentsExtension androidComponents = project.getExtensions() + .getByType(ApplicationAndroidComponentsExtension.class); - // Create tasks for each variant final Boolean[] isCLITaskCreated = new Boolean[1]; isCLITaskCreated[0] = false; - appVariants.all(applicationVariant -> { - String applicationVariantName = null; - try { - applicationVariantName = Tools.capitalize(applicationVariant.getName()); - } catch (Exception e) { - return; - } - - // Since we can't use an outer variable in lambda expression which is not final. - final String appVariantName = applicationVariantName; - project.getTasks().create("execute" + appVariantName + "TestsOnBrowserstack", EspressoTask.class, (task) -> { - task.setGroup(DEFAULT_GROUP); - task.setDescription("Uploads app / tests to AppAutomate and executes them"); - // Run Espresso tests without building the apk and test apk - if (!project.hasProperty("skipBuildingApks") || Boolean.parseBoolean(project.property("skipBuildingApks").toString()) == false) { - task.dependsOn("assemble" + appVariantName, "assemble" + appVariantName + "AndroidTest"); + androidComponents.onVariants(androidComponents.selector(), new Action() { + @Override + public void execute(ApplicationVariant applicationVariant) { + String applicationVariantName = null; + try { + applicationVariantName = Tools.capitalize(applicationVariant.getName()); + } catch (Exception e) { + return; } - task.setAppVariantBaseName(applicationVariant.getBaseName()); - task.setUsername(browserStackConfigExtension.getUsername()); - task.setAccessKey(browserStackConfigExtension.getAccessKey()); - task.setCustomId(browserStackConfigExtension.getCustomId()); - task.setConfigFilePath(browserStackConfigExtension.getConfigFilePath()); - task.setHost(Constants.BROWSERSTACK_API_HOST); - task.setDebug(browserStackConfigExtension.isDebug()); - if(project.hasProperty("mainAPKPath")){ - task.setMainAPKPath(project.property("mainAPKPath").toString()); - } - if(project.hasProperty("testAPKPath")){ - task.setTestAPKPath(project.property("testAPKPath").toString()); - } - }); - project.getTasks().create("upload" + appVariantName + "ToBrowserstackAppLive", AppLiveUploadTask.class, (task) -> { - task.setGroup(DEFAULT_GROUP); - task.setDescription("Uploads app to AppLive"); - task.dependsOn("assemble" + appVariantName); - task.setAppVariantBaseName(applicationVariant.getBaseName()); - task.setHost(Constants.BROWSERSTACK_API_HOST); - task.setUsername(browserStackConfigExtension.getUsername()); - task.setAccessKey(browserStackConfigExtension.getAccessKey()); - task.setCustomId(browserStackConfigExtension.getCustomId()); - task.setDebug(browserStackConfigExtension.isDebug()); - }); + final String appVariantName = applicationVariantName; + project.getTasks().create("execute" + appVariantName + "TestsOnBrowserstack", EspressoTask.class, + (task) -> { + task.setGroup(DEFAULT_GROUP); + task.setDescription("Uploads app / tests to AppAutomate and executes them"); + // Unless skipBuildingApks, depend on assemble tasks for app and test APKs. + if (!project.hasProperty("skipBuildingApks") + || Boolean.parseBoolean(project.property("skipBuildingApks").toString()) == false) { + task.dependsOn("assemble" + appVariantName, + "assemble" + appVariantName + "AndroidTest"); + } + task.setAppVariantBaseName(applicationVariant.getName()); + task.setUsername(browserStackConfigExtension.getUsername()); + task.setAccessKey(browserStackConfigExtension.getAccessKey()); + task.setCustomId(browserStackConfigExtension.getCustomId()); + task.setConfigFilePath(browserStackConfigExtension.getConfigFilePath()); + task.setHost(Constants.BROWSERSTACK_API_HOST); + task.setDebug(browserStackConfigExtension.isDebug()); + if (project.hasProperty("mainAPKPath")) { + task.setMainAPKPath(project.property("mainAPKPath").toString()); + } + if (project.hasProperty("testAPKPath")) { + task.setTestAPKPath(project.property("testAPKPath").toString()); + } + }); - project.getTasks().create("upload" + appVariantName + "ToBrowserstackAppAutomate", AppAutomateUploadTask.class, (task) -> { - task.setGroup(DEFAULT_GROUP); - task.setDescription("Uploads app to AppAutomate"); - task.dependsOn("assemble" + appVariantName); - task.setAppVariantBaseName(applicationVariant.getBaseName()); - task.setHost(Constants.BROWSERSTACK_API_HOST); - task.setUsername(browserStackConfigExtension.getUsername()); - task.setAccessKey(browserStackConfigExtension.getAccessKey()); - task.setCustomId(browserStackConfigExtension.getCustomId()); - task.setDebug(browserStackConfigExtension.isDebug()); - }); - if (!isCLITaskCreated[0]) { - project.getTasks().create("browserstackCLIWrapper", CLI.class, (task) -> { - task.setGroup(DEFAULT_GROUP); - task.setDescription("Just a wrapper on the Browserstack CLI. A way to run any Browserstack CLI command directly from gradle. \n" + - "\n" + - "\n" + - "For reference on Browserstack CLI please visit https://www.browserstack.com/app-automate/browserstack-cli\n" + - "\n" + - "\n" + - "Any CLI command passed in the custom option -Pcommand will be executed and the results will be displayed on the terminal.\n" + - "\n" + - "\n" + - "For example:\n" + - "\n" + - "gradle browserstackCLIWrapper -Pcommand=”app-automate apps”\n" + - "\n" + - "The browserstack CLI command app-automate apps would run and the result will be displayed on the terminal. "); + project.getTasks().create("upload" + appVariantName + "ToBrowserstackAppLive", AppLiveUploadTask.class, + (task) -> { + task.setGroup(DEFAULT_GROUP); + task.setDescription("Uploads app to AppLive"); + task.dependsOn("assemble" + appVariantName); + task.setAppVariantBaseName(applicationVariant.getName()); + task.setHost(Constants.BROWSERSTACK_API_HOST); + task.setUsername(browserStackConfigExtension.getUsername()); + task.setAccessKey(browserStackConfigExtension.getAccessKey()); + task.setCustomId(browserStackConfigExtension.getCustomId()); + task.setDebug(browserStackConfigExtension.isDebug()); + }); - task.dependsOn("assemble" + appVariantName); - task.setAppVariantBaseName(applicationVariant.getBaseName()); - task.setHost(Constants.BROWSERSTACK_API_HOST); - task.setUsername(browserStackConfigExtension.getUsername()); - task.setAccessKey(browserStackConfigExtension.getAccessKey()); - task.setCustomId(browserStackConfigExtension.getCustomId()); - task.setDebug(browserStackConfigExtension.isDebug()); - if (project.hasProperty("command")) { - System.out.println("Command found: " + project.findProperty("command").toString()); - task.setCommand(project.property("command").toString()); - } - }); - isCLITaskCreated[0] = true; + project.getTasks().create("upload" + appVariantName + "ToBrowserstackAppAutomate", + AppAutomateUploadTask.class, (task) -> { + task.setGroup(DEFAULT_GROUP); + task.setDescription("Uploads app to AppAutomate"); + task.dependsOn("assemble" + appVariantName); + task.setAppVariantBaseName(applicationVariant.getName()); + task.setHost(Constants.BROWSERSTACK_API_HOST); + task.setUsername(browserStackConfigExtension.getUsername()); + task.setAccessKey(browserStackConfigExtension.getAccessKey()); + task.setCustomId(browserStackConfigExtension.getCustomId()); + task.setDebug(browserStackConfigExtension.isDebug()); + }); + if (!isCLITaskCreated[0]) { + project.getTasks().create("browserstackCLIWrapper", CLI.class, (task) -> { + task.setGroup(DEFAULT_GROUP); + task.setDescription( + "Just a wrapper on the Browserstack CLI. A way to run any Browserstack CLI command directly from gradle. \n" + + + "\n" + + "\n" + + "For reference on Browserstack CLI please visit https://www.browserstack.com/app-automate/browserstack-cli\n" + + + "\n" + + "\n" + + "Any CLI command passed in the custom option -Pcommand will be executed and the results will be displayed on the terminal.\n" + + + "\n" + + "\n" + + "For example:\n" + + "\n" + + "gradle browserstackCLIWrapper -Pcommand=”app-automate apps”\n" + + "\n" + + "The browserstack CLI command app-automate apps would run and the result will be displayed on the terminal. "); + + task.dependsOn("assemble" + appVariantName); + task.setAppVariantBaseName(applicationVariant.getName()); + task.setHost(Constants.BROWSERSTACK_API_HOST); + task.setUsername(browserStackConfigExtension.getUsername()); + task.setAccessKey(browserStackConfigExtension.getAccessKey()); + task.setCustomId(browserStackConfigExtension.getCustomId()); + task.setDebug(browserStackConfigExtension.isDebug()); + if (project.hasProperty("command")) { + System.out.println("Command found: " + project.findProperty("command").toString()); + task.setCommand(project.property("command").toString()); + } + }); + isCLITaskCreated[0] = true; + } } }); } diff --git a/src/main/java/com/browserstack/gradle/BrowserStackSDKPlugin.java b/src/main/java/com/browserstack/gradle/BrowserStackSDKPlugin.java index fc0db50..5080765 100644 --- a/src/main/java/com/browserstack/gradle/BrowserStackSDKPlugin.java +++ b/src/main/java/com/browserstack/gradle/BrowserStackSDKPlugin.java @@ -13,8 +13,18 @@ import org.gradle.api.execution.TaskExecutionGraph; +/** + * Gradle plugin for BrowserStack SDK integration. Writes gradle-m-config.json with project/task + * context for the SDK and disables JUnit XML/HTML test reports when running under platformIndex. + */ public class BrowserStackSDKPlugin implements Plugin { String fileSeparator = System.getProperty("file.separator"); + + /** + * Applies the plugin: adds doFirst to all tasks to write config, and whenReady disables test reports if needed. + * + * @param project the Gradle project + */ public void apply(Project project) { project.getTasks().withType(Task.class, task -> { task.doFirst(new Action() { @@ -58,8 +68,8 @@ public void execute(TaskExecutionGraph taskGraph) { @Override public void execute(Test test) { try { - test.getReports().getJunitXml().setEnabled(false); - test.getReports().getHtml().setEnabled(false); + test.getReports().getJunitXml().getRequired().set(false); + test.getReports().getHtml().getRequired().set(false); } catch (Throwable e) {} } }); diff --git a/src/main/java/com/browserstack/gradle/BrowserStackTask.java b/src/main/java/com/browserstack/gradle/BrowserStackTask.java index 696ad7a..7994d21 100644 --- a/src/main/java/com/browserstack/gradle/BrowserStackTask.java +++ b/src/main/java/com/browserstack/gradle/BrowserStackTask.java @@ -20,11 +20,17 @@ import org.jetbrains.annotations.NotNull; import org.gradle.api.tasks.Optional; - +/** + * Base task for BrowserStack operations. Handles credentials, app upload, and APK path resolution. + * Subclasses implement specific actions (Espresso run, App Live upload, App Automate upload, CLI). + */ public class BrowserStackTask extends DefaultTask { + /** Extra property key for custom_id when uploading apps. */ public static final String KEY_EXTRA_CUSTOM_ID = "custom_id"; + /** Map key for the main/debug APK path in locateApks result. */ public static final String KEY_FILE_DEBUG = "debugApkPath"; + /** Map key for the test APK path in locateApks result. */ public static final String KEY_FILE_TEST = "testApkPath"; @Input @@ -69,6 +75,10 @@ public void setAccessKey(String accessKey) { this.accessKey = accessKey; } + public String getCustomId() { + return customId; + } + public void setCustomId(String customId) { this.customId = customId; } @@ -81,6 +91,10 @@ public String getHost() { return host; } + public String getApp() { + return app; + } + public void setHost(String host) { this.host = host; } @@ -97,10 +111,15 @@ public void setHost(String host) { public void setTestAPKPath(String testAPKPath) { this.testAPKPath = testAPKPath; } + /** + * Builds the default JSON params for BrowserStack API (app URL, source tag). + * + * @return params object for the build request + */ protected JSONObject constructDefaultBuildParams() { JSONObject params = new JSONObject(); params.put("app", app); - // for monitoring, not for external use + // For monitoring; not for external use. params.put("browserstack.source", "gradlePlugin"); return params; @@ -157,6 +176,12 @@ public String basicAuth() { return "Basic " + Base64.getEncoder().encodeToString((username + ":" + accessKey).getBytes()); } + /** + * Returns the path with the latest modification time from the list. + * + * @param paths list of file paths + * @return the most recently modified path, or null if list is empty + */ public static Path findMostRecentPath(List paths) { long mostRecentTime = 0L; Path mostRecentPath = null; @@ -181,13 +206,21 @@ private String getAbsolutePath(String apkPath, String currentWorkingDirectory){ } return apkPath; } + /** + * Resolves main and test APK paths from mainAPKPath/testAPKPath or by scanning the project. + * + * @param ignoreTestPath if true, test APK may be null (e.g. for App Live upload) + * @return map with KEY_FILE_DEBUG and KEY_FILE_TEST paths + * @throws IOException if required APKs cannot be found + */ public Map locateApks(boolean ignoreTestPath) throws IOException { Path debugApkPath; Path testApkPath; String dir = System.getProperty("user.dir"); List appApkFiles = new ArrayList<>(); List testApkFiles = new ArrayList<>(); - final Boolean[] isAPKFileCreated = {false,false}; // 1st element stores true if main apk is read from path provided by client and false otherwise. 2nd element is for test apk. + // First element: true if main APK was from client path. Second: true if test APK was from client path. + final Boolean[] isAPKFileCreated = {false, false}; if(mainAPKPath != null){ isAPKFileCreated[0] = true; try { @@ -233,7 +266,7 @@ public Map locateApks(boolean ignoreTestPath) throws IOException { throw new IOException("unable to find DebugApp apk"); } - //Dont raise error for testApkPath if AppLive task + // Don't raise error for testApkPath if App Live task (ignoreTestPath true). if (!ignoreTestPath && testApkPath == null) { throw new IOException("unable to find TestApp apk"); } diff --git a/src/main/java/com/browserstack/gradle/CLI.java b/src/main/java/com/browserstack/gradle/CLI.java index ba633ca..072cb68 100644 --- a/src/main/java/com/browserstack/gradle/CLI.java +++ b/src/main/java/com/browserstack/gradle/CLI.java @@ -12,6 +12,10 @@ import java.util.regex.Pattern; import java.util.regex.Matcher; +/** + * Gradle task that downloads and runs the BrowserStack CLI for the current platform. + * Requires username and accessKey (from extension or env). + */ public class CLI extends BrowserStackTask { private Boolean isWindows; @@ -21,6 +25,7 @@ public class CLI extends BrowserStackTask { private String arch; private String os; + /** Ensures username and accessKey are set. */ public void verifyParams() throws Exception { String username = this.getUsername(); String accessKey = this.getAccessKey(); @@ -132,7 +137,7 @@ private Process runProcess(String command) { List list = new ArrayList<>(); Matcher matcher = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(command); while (matcher.find()) - list.add(matcher.group(1).replace("\"", "")); // Adding .replace("\"", "") to remove surrounding quotes. + list.add(matcher.group(1).replace("\"", "")); // Strip surrounding quotes from each token. try { process = Runtime.getRuntime().exec(list.toArray(new String[0]), null, new File(directory)); @@ -155,8 +160,7 @@ private void install() { FileOutputStream fos = new FileOutputStream(new File(directory + "/" + downloadedFileName)); int length = -1; - byte[] buffer = new byte[1024];// buffer for portion of data from - // connection + byte[] buffer = new byte[1024]; // Buffer for reading from the connection. while ((length = in.read(buffer)) > -1) { fos.write(buffer, 0, length); } diff --git a/src/main/java/com/browserstack/gradle/Constants.java b/src/main/java/com/browserstack/gradle/Constants.java index a01b870..3aac310 100644 --- a/src/main/java/com/browserstack/gradle/Constants.java +++ b/src/main/java/com/browserstack/gradle/Constants.java @@ -1,5 +1,6 @@ package com.browserstack.gradle; +/** BrowserStack API hosts, URL paths, CLI version, and default option constants. */ public class Constants { public static final String BROWSERSTACK_API_HOST = "https://api-cloud.browserstack.com", diff --git a/src/main/java/com/browserstack/gradle/EspressoTask.java b/src/main/java/com/browserstack/gradle/EspressoTask.java index 9d49062..ec8322f 100644 --- a/src/main/java/com/browserstack/gradle/EspressoTask.java +++ b/src/main/java/com/browserstack/gradle/EspressoTask.java @@ -1,4 +1,4 @@ - package com.browserstack.gradle; +package com.browserstack.gradle; import org.gradle.api.tasks.TaskAction; import java.io.FileReader; @@ -12,6 +12,10 @@ import org.gradle.api.tasks.Input; import org.gradle.api.tasks.options.Option; +/** + * Gradle task that runs Espresso tests on BrowserStack App Automate. + * Uses a JSON config file (default or via --config-file) and uploads app + test APKs. + */ public class EspressoTask extends BrowserStackTask { @Input diff --git a/src/main/java/com/browserstack/gradle/Tools.java b/src/main/java/com/browserstack/gradle/Tools.java index b8b71aa..4bb1749 100644 --- a/src/main/java/com/browserstack/gradle/Tools.java +++ b/src/main/java/com/browserstack/gradle/Tools.java @@ -1,11 +1,19 @@ package com.browserstack.gradle; +/** Shared string and naming utilities for the BrowserStack Gradle plugin. */ public class Tools { + /** Returns true if the string is null or has length zero. */ public static boolean isStringEmpty(String str) { return str == null || str.length() == 0; } + /** + * Returns the string with the first character uppercased. + * @param variantName non-null, non-empty string + * @return capitalized string + * @throws Exception if variantName is null or empty + */ public static String capitalize(String variantName) throws Exception { if (isStringEmpty(variantName)) { throw new Exception("Null or empty variantName passed."); diff --git a/src/main/java/com/browserstack/httputils/HttpUtils.java b/src/main/java/com/browserstack/httputils/HttpUtils.java index 7844a55..63d3961 100644 --- a/src/main/java/com/browserstack/httputils/HttpUtils.java +++ b/src/main/java/com/browserstack/httputils/HttpUtils.java @@ -1,6 +1,5 @@ package com.browserstack.httputils; -import com.android.annotations.NonNull; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -15,15 +14,15 @@ public class HttpUtils { /** - * Uploads a file and binds custom data - * @param isDebug enabled debugging logs when forming a request: - * @param wrapPropsAsInternalDataMap indicates extra properties to be wrapped into internal data map - * @param url endpoint url + * Uploads a file and binds custom data. + * @param isDebug when true, enables debug logging when forming the request + * @param wrapPropsAsInternalDataMap whether extra properties are wrapped into the internal data map + * @param url endpoint URL * @param authorization authorization token - * @param appPath raw file path - * @param properties extra properties bind to request - * @return connection - * @throws IOException error connecting / sending request + * @param appPath path to the app file + * @param properties extra properties to bind to the request + * @return the opened connection + * @throws IOException if connecting or sending the request fails */ public static HttpURLConnection sendPostApp( boolean isDebug, @@ -31,7 +30,7 @@ public static HttpURLConnection sendPostApp( @NotNull String url, @Nullable String authorization, @NotNull String appPath, - @NonNull Map properties + @NotNull Map properties ) throws IOException { final OutputWriterDebug debugWriter = OutputWriterDebug.withDebugEnabled(isDebug); final RequestBoundary requestBoundary = RequestBoundary.generate(); diff --git a/src/main/java/com/browserstack/httputils/MultipartRequestComposer.java b/src/main/java/com/browserstack/httputils/MultipartRequestComposer.java index 0902a9f..fd962f6 100644 --- a/src/main/java/com/browserstack/httputils/MultipartRequestComposer.java +++ b/src/main/java/com/browserstack/httputils/MultipartRequestComposer.java @@ -1,6 +1,5 @@ package com.browserstack.httputils; -import com.android.annotations.NonNull; import com.browserstack.json.JSONObject; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,7 +32,7 @@ public MultipartRequestComposer(@NotNull Builder builder) { } private MultipartRequestComposer() { - // Cannot build from empty constructor + // Not for use; use Builder instead. throw new IllegalArgumentException(); } @@ -133,7 +132,7 @@ public Builder putFileFromPath(@NotNull String filePath) { } public Builder putKeyValue( - @NonNull String key, + @NotNull String key, @Nullable String value ) { if (value != null) { @@ -143,7 +142,7 @@ public Builder putKeyValue( } public Builder putProperties( - @NonNull Map properties + @NotNull Map properties ) { for (Map.Entry entry : properties.entrySet()) { if (entry.getValue() != null) { @@ -154,10 +153,10 @@ public Builder putProperties( } /** - * Indicates to either wrap all additional properties as an internal data map - * or use as regular properties when forming a reuqest - * @param isWrapEnabled is wrapping enabled - * @return builder + * When true, wraps all additional properties in an internal data map; when false, uses them as regular form fields. + * + * @param isWrapEnabled whether wrapping is enabled + * @return this builder */ public Builder wrapProperiesAsInternalDataMap(boolean isWrapEnabled) { this.wrapProperiesAsInternalDataMap = isWrapEnabled; diff --git a/src/main/java/com/browserstack/httputils/RequestBoundary.java b/src/main/java/com/browserstack/httputils/RequestBoundary.java index 5e3f587..429fba1 100644 --- a/src/main/java/com/browserstack/httputils/RequestBoundary.java +++ b/src/main/java/com/browserstack/httputils/RequestBoundary.java @@ -6,8 +6,7 @@ import java.util.Random; /** - * Generates and wraps boundary - * Useful for multipart request + * Generates and wraps a boundary string for multipart requests. */ public class RequestBoundary { diff --git a/src/main/resources/META-INF/gradle-plugins/com.browserstack.gradle-sdk.properties b/src/main/resources/META-INF/gradle-plugins/com.browserstack.gradle-sdk.properties deleted file mode 100644 index 502f36d..0000000 --- a/src/main/resources/META-INF/gradle-plugins/com.browserstack.gradle-sdk.properties +++ /dev/null @@ -1 +0,0 @@ -implementation-class=com.browserstack.gradle.BrowserStackSDKPlugin diff --git a/src/main/resources/META-INF/gradle-plugins/com.browserstack.gradle.properties b/src/main/resources/META-INF/gradle-plugins/com.browserstack.gradle.properties deleted file mode 100644 index 5a74438..0000000 --- a/src/main/resources/META-INF/gradle-plugins/com.browserstack.gradle.properties +++ /dev/null @@ -1 +0,0 @@ -implementation-class=com.browserstack.gradle.BrowserStackPlugin diff --git a/test.rb b/test.rb index 63f2f40..8ff3873 100644 --- a/test.rb +++ b/test.rb @@ -149,29 +149,29 @@ def run_tests_args def run_tests_with_path_args puts "\nRunning new test using ./gradlew with APK paths for main and test apk" mainAPKPath = $current_path + "/test/mainApk" - testAPKPAth = $current_path + "/test/testApk" - run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth}") + testAPKPath = $current_path + "/test/testApk" + run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath}") end def run_tests_with_relative_path puts "\nRunning new test using ./gradlew with relative paths for both main and test apk" mainAPKPath = "./test/mainApk" - testAPKPAth = "./test/testApk" - run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth}") + testAPKPath = "./test/testApk" + run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath}") end def run_test_with_incorrect_path puts "\nRunning new test using ./gradlew with incorrect main and test APK paths" mainAPKPath = $current_path - testAPKPAth = $current_path - run_espresso_test_with_incorrect_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth}") + testAPKPath = $current_path + run_espresso_test_with_incorrect_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath}") end def run_test_with_incorrect_relative_path puts "\nRunning new test using ./gradlew with incorrect relative paths for both main and test APK " mainAPKPath = "./test" - testAPKPAth = "./test" - run_espresso_test_with_incorrect_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth}") + testAPKPath = "./test" + run_espresso_test_with_incorrect_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath}") end @@ -180,8 +180,8 @@ def run_tests_with_path_variations mainAPKPath = $current_path + "/test/mainApk" run_espresso_test_with_either_main_or_test_apk_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath}", mainAPKPath); puts "\nRunning new test using ./gradlew with testAPKPath arg only" - testAPKPAth = $current_path + "/test/testApk" - run_espresso_test_with_either_main_or_test_apk_path("./gradlew executeDebugTestsOnBrowserstack -PtestAPKPath=#{testAPKPAth}", testAPKPAth); + testAPKPath = $current_path + "/test/testApk" + run_espresso_test_with_either_main_or_test_apk_path("./gradlew executeDebugTestsOnBrowserstack -PtestAPKPath=#{testAPKPath}", testAPKPath); end def run_tests_with_relative_path_variations @@ -189,15 +189,15 @@ def run_tests_with_relative_path_variations mainAPKPath = "./test/mainApk" run_espresso_test_with_either_main_or_test_apk_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath}", mainAPKPath); puts "\nRunning new test using ./gradlew with testAPKPath(relative path) arg only" - testAPKPAth = "./test/testApk" - run_espresso_test_with_either_main_or_test_apk_path("./gradlew executeDebugTestsOnBrowserstack -PtestAPKPath=#{testAPKPAth}", testAPKPAth); + testAPKPath = "./test/testApk" + run_espresso_test_with_either_main_or_test_apk_path("./gradlew executeDebugTestsOnBrowserstack -PtestAPKPath=#{testAPKPath}", testAPKPath); puts "\nRunning with absolute main and relative test path" mainAPKPath = $current_path + "/test/mainApk" - run_espresso_test_with_one_absolute_and_one_relative_apk_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth}", mainAPKPath, testAPKPAth) + run_espresso_test_with_one_absolute_and_one_relative_apk_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath}", mainAPKPath, testAPKPath) puts "\nRunning with absolute test and relative main path" - testAPKPAth = $current_path + "/test/testApk" + testAPKPath = $current_path + "/test/testApk" mainAPKPath = "./test/mainApk" - run_espresso_test_with_one_absolute_and_one_relative_apk_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth}", mainAPKPath, testAPKPAth) + run_espresso_test_with_one_absolute_and_one_relative_apk_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath}", mainAPKPath, testAPKPath) end def run_espresso_test_with_non_existing_apk_path(gradle_command, apk_type) @@ -274,15 +274,15 @@ def run_test_with_one_correct_and_one_non_existing_path def run_test_with_ipa puts "Running tests with ipa files" mainAPKPath = $current_path + "/test/mainApk/ipa" - testAPKPAth = $current_path + "/test/testApk/ipa" - run_espresso_test_with_incorrect_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth}") + testAPKPath = $current_path + "/test/testApk/ipa" + run_espresso_test_with_incorrect_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath}") end def run_test_with_zip - puts "Running tests with ipa files" + puts "Running tests with zip files" mainAPKPath = $current_path + "/test/mainApk/" - testAPKPAth = $current_path + "/test/mainApk/zip" - run_espresso_test_with_one_incorrect_apk_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth}"); + testAPKPath = $current_path + "/test/mainApk/zip" + run_espresso_test_with_one_incorrect_apk_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath}"); end def run_tests_with_flavors @@ -306,8 +306,8 @@ def build_plugin def validate_env missing_env_variables = [] - requied_env_variables = ["ANDROID_HOME","BROWSERSTACK_USERNAME", "BROWSERSTACK_ACCESS_KEY"] - requied_env_variables.each do |env_variable| + required_env_variables = ["ANDROID_HOME","BROWSERSTACK_USERNAME", "BROWSERSTACK_ACCESS_KEY"] + required_env_variables.each do |env_variable| if ENV[env_variable].nil? missing_env_variables += [env_variable] end @@ -352,31 +352,31 @@ def run_cli_test_delete_command(gradle_command) def run_test_with_cucumber_options puts "\nRunning new test using ./gradlew with absolute APK paths with cucumber options" mainAPKPath = $current_path + "/test/mainApk/cucumber" - testAPKPAth = $current_path + "/test/testApk/cucumber" - run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth} --config-file=config-browserstack_cucumber.json") + testAPKPath = $current_path + "/test/testApk/cucumber" + run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath} --config-file=config-browserstack_cucumber.json") puts "\nRunning new test using ./gradlew with absolute APK paths with invalid cucumber options" - run_espresso_test_with_invalid_cucumber_config("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth} --config-file=config-browserstack_cucumber_invalid_name.json") + run_espresso_test_with_invalid_cucumber_config("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath} --config-file=config-browserstack_cucumber_invalid_name.json") puts "\nRunning new test using ./gradlew with relative APK paths with cucumber options" mainAPKPath = "./test/mainApk/cucumber" - testAPKPAth = "./test/testApk/cucumber" - run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth} --config-file=config-browserstack_cucumber.json") + testAPKPath = "./test/testApk/cucumber" + run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath} --config-file=config-browserstack_cucumber.json") puts "\nRunning new test using ./gradlew with relative APK paths with invalid cucumber options" - run_espresso_test_with_invalid_cucumber_config("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth} --config-file=config-browserstack_cucumber_invalid_name.json") + run_espresso_test_with_invalid_cucumber_config("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath} --config-file=config-browserstack_cucumber_invalid_name.json") end def run_test_with_instrumentation_options puts "\nRunning new test using ./gradlew with absolute APK paths with instrumentation options" mainAPKPath = $current_path + "/test/mainApk/cucumber" - testAPKPAth = $current_path + "/test/testApk/cucumber" - run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth} --config-file=config-browserstack_instrumentation.json") + testAPKPath = $current_path + "/test/testApk/cucumber" + run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath} --config-file=config-browserstack_instrumentation.json") puts "\nRunning new test using ./gradlew with absolute APK paths with invalid instrumentation options" - run_espresso_test_with_invalid_instrumentation_config("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth} --config-file=config-browserstack_instrumentation_invalid.json") + run_espresso_test_with_invalid_instrumentation_config("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath} --config-file=config-browserstack_instrumentation_invalid.json") puts "\nRunning new test using ./gradlew with relative APK paths with instrumentation options" mainAPKPath = "./test/mainApk/cucumber" - testAPKPAth = "./test/testApk/cucumber" - run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth} --config-file=config-browserstack_instrumentation.json") + testAPKPath = "./test/testApk/cucumber" + run_espresso_test_with_path("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath} --config-file=config-browserstack_instrumentation.json") puts "\nRunning new test using ./gradlew with relative APK paths with invalid instrumentation options" - run_espresso_test_with_invalid_instrumentation_config("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPAth} --config-file=config-browserstack_instrumentation_invalid.json") + run_espresso_test_with_invalid_instrumentation_config("./gradlew executeDebugTestsOnBrowserstack -PmainAPKPath=#{mainAPKPath} -PtestAPKPath=#{testAPKPath} --config-file=config-browserstack_instrumentation_invalid.json") end def test