Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .github/workflows/sdk-sample-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Runs the BrowserStack SDK sample against a given commit and reports a status check.
# Trigger: Actions tab -> "Gauge Appium App Automate SDK sample test" -> Run workflow -> paste the PR's full commit SHA.
# Requires repo secrets: BROWSERSTACK_USERNAME, BROWSERSTACK_ACCESS_KEY.
# NOTE (App Automate): the app under test is referenced via `app: bs://...` in browserstack.yml;
# ensure that uploaded app exists on the account whose secrets are used (re-upload + update if expired).
name: Gauge Appium App Automate SDK sample test

on:
workflow_dispatch:
inputs:
commit_sha:
description: 'The full commit id to build'
required: true

jobs:
sdk-sample:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
max-parallel: 3
matrix:
os: [ubuntu-latest]
java: ['11', '17']
name: gauge-appium JDK ${{ matrix.java }} sample
env:
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
defaults:
run:
working-directory: android
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.commit_sha }}
- name: Mark status check in_progress
uses: actions/github-script@v7
env:
job_name: gauge-appium JDK ${{ matrix.java }} sample
commit_sha: ${{ github.event.inputs.commit_sha }}
with:
github-token: ${{ github.token }}
script: |
await github.rest.checks.create({
owner: context.repo.owner, repo: context.repo.repo,
name: process.env.job_name, head_sha: process.env.commit_sha,
status: 'in_progress'
}).catch(e => console.log('check create failed:', e.status));
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
- name: Install Gauge
run: |
npm install -g @getgauge/cli
gauge install java
- name: Run sample test
run: |
mvn compile
mvn test
- name: Mark status check completed
if: always()
uses: actions/github-script@v7
env:
conclusion: ${{ job.status }}
job_name: gauge-appium JDK ${{ matrix.java }} sample
commit_sha: ${{ github.event.inputs.commit_sha }}
with:
github-token: ${{ github.token }}
script: |
await github.rest.checks.create({
owner: context.repo.owner, repo: context.repo.repo,
name: process.env.job_name, head_sha: process.env.commit_sha,
status: 'completed', conclusion: process.env.conclusion
}).catch(e => console.log('check create failed:', e.status));

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
Comment on lines +17 to +74
31 changes: 31 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Gradle
.gradle/
**/build/
gradle-app.setting

# Gauge
**/logs/
**/log/
**/reports/
**/.gauge/
local.log

# BrowserStack SDK run artifacts
**/browserstack.err
**/bstack_build_*/
**/gradle-m-config.json
**/dependency-tree.log

# IDE
.idea/
*.iml
.vscode/
.settings/
.project
.classpath

# OS
.DS_Store

# Credentials — never commit
*.env
119 changes: 117 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,117 @@
# gauge-appium-app-browserstack
We require the following new public repositories under the browserstack GitHub organization to host customer-facing sample projects for the BrowserStack SDK.
# Gauge + Appium with BrowserStack App Automate

Run [Gauge](https://gauge.org/) + [Appium](https://appium.io/) (Java) mobile tests on real devices
in the [BrowserStack App Automate](https://app-automate.browserstack.com/) cloud, instrumented by the
[BrowserStack Java SDK](https://www.browserstack.com/docs/app-automate/appium/getting-started/java).

The SDK reads `browserstack.yml`, uploads/points at the app under test, provisions the device, and reports
each scenario's name and status to App Automate and Test Observability — no per-test capability code.

This sample currently ships the **`android/`** platform directory (Android, Samsung Galaxy S22 Ultra).

## Prerequisites

- A [BrowserStack](https://www.browserstack.com/) account (username + access key).
- **JDK 11+** (`java -version`).
- **Gradle 7+** (a wrapper can be generated with `gradle wrapper`).
- **Gauge CLI** with the Java plugin:
```bash
brew install gauge # macOS (or see https://docs.gauge.org/getting_started/installing-gauge.html)
gauge install java
gauge install html-report
gauge install screenshot
```

## Setup

```bash
git clone <this-repo>
cd gauge-appium/android
```

Configure credentials — set them as environment variables (recommended) or edit `android/browserstack.yml`:

```bash
export BROWSERSTACK_USERNAME="YOUR_USERNAME"
export BROWSERSTACK_ACCESS_KEY="YOUR_ACCESS_KEY"
```

Resolve dependencies and stage the BrowserStack Java SDK agent:

```bash
gradle clean build copyBrowserStackAgent
```

`copyBrowserStackAgent` stages the resolved `browserstack-java-sdk` jar at `build/agent/browserstack-java-sdk.jar`.
The `-javaagent` is attached to the **Gauge Java runner JVM** via `env/default/java.properties`
(`gauge_jvm_args`) — Gauge spawns its own runner JVM, so the agent must be wired there (not on the Gradle
build JVM) for the Appium driver to be instrumented.

### App under test

`android/browserstack.yml` points at a pre-uploaded `WikipediaSample.apk`
(`app: bs://92d48b416632f2b1734259565ceab61b05ad0b24`). To use your own build, upload it and replace the value:

```bash
curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/upload" \
-F "file=@/path/to/WikipediaSample.apk"
# -> use the returned "app_url" (bs://...) as the `app:` value
```

## Run Sample Test

Searches the Wikipedia sample app for "BrowserStack" and asserts results are listed.

```bash
cd android
gradle runSampleTest
# or run the whole suite: gradle gauge
# or via the Gauge CLI: gauge run specs/bstack-specs/bstack-sample.spec
```

## Run Local Test

Verifies the [BrowserStack Local](https://www.browserstack.com/local-testing/app-automate) tunnel is
connected. The SDK starts the tunnel automatically because `browserstackLocal: true` is set in
`browserstack.yml`.

```bash
cd android
gradle runLocalTest
# or via the Gauge CLI: gauge run specs/bstack-specs/local-test.spec
```

## Notes / Dashboard

- View runs, video, device logs, and network logs at **https://app-automate.browserstack.com/**.
- With `testObservability: true`, builds also appear at **https://observability.browserstack.com/**.
- The `framework: gauge` token in `browserstack.yml` lets the Java SDK report each scenario's name and
status to BrowserStack.
- The BrowserStack Gradle SDK plugin (`com.browserstack.gradle-sdk`) is declared per the BrowserStack
SDK convention; the actual Appium-driver instrumentation for this Gauge/Java flow is performed by the
`browserstack-java-sdk` `-javaagent` on the Gauge runner JVM (see Setup).

### Known issue (BrowserStack Java SDK + Gauge App Automate)

With the **published** `browserstack-java-sdk:1.59.7`, `framework: gauge` disables the SDK's normal
in-process flow and instead drives the run through the **Gradle Tooling API** (it re-invokes the
`gauge` task per device platform). On this version the SDK mis-resolves the Gradle installation to
`GRADLE_USER_HOME` (`~/.gradle`) and the Tooling-API connection fails:

```
GradleTaskExecutor - Using Gradle Installation /Users/<you>/.gradle
GradleTaskExecutor - Gradle connection failed for Platform Index: 0
[SDK-TRA-006] ... could not find relevant classes from gauge on your class path
```

The SDK then ends its run inside the `-javaagent` premain and the Gauge runner JVM exits before any
test (and therefore any Appium driver / device session) is created. Only the Observability/TestHub
build is created; no App Automate device session starts.

This reproduces with both `gauge run` and `gradle gauge`, with `gauge-java` 0.12.0 and 1.0.1, and with
or without the `framework` token. The known-working internal reference for this combo used **unpublished
local dev builds** (`browserstack-java-sdk-1.32.12` + `gradle-sdk` plugin `99.86.49`). Track the fix /
a published known-good SDK version with the BrowserStack Java SDK team before relying on this sample for
a live device run. The repo itself is complete and compiles; the blocker is in the SDK's Gauge App
Automate execution path, not in the test code.
60 changes: 60 additions & 0 deletions android/browserstack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# =============================
# Set BrowserStack Credentials
# =============================
# Add your BrowserStack userName and accessKey here or set BROWSERSTACK_USERNAME and
# BROWSERSTACK_ACCESS_KEY as env variables.
userName: YOUR_USERNAME
accessKey: YOUR_ACCESS_KEY

# ======================
# BrowserStack Reporting
# ======================
projectName: BrowserStack Samples
buildName: appauto-gauge-appium
buildIdentifier: '#${BUILD_NUMBER}'
# `framework` lets the BrowserStack Java SDK (javaagent) instrument the Gauge runner and report each
# scenario's name and status to BrowserStack. The SDK's gauge instrumentation is validated against the
# Gauge `java` runner plugin + gauge-java library 0.12.0 (see build.gradle); keep those aligned.
framework: gauge

# ======================================
# App under test (pre-uploaded bs:// id)
# ======================================
# WikipediaSample.apk, already uploaded to BrowserStack App Automate.
# To upload your own build, run:
# curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \
# -X POST "https://api-cloud.browserstack.com/app-automate/upload" \
# -F "file=@/path/to/WikipediaSample.apk"
# then replace the value below with the returned app_url (bs://...).
app: bs://92d48b416632f2b1734259565ceab61b05ad0b24

# =======================================
# Platforms (real devices to test on)
# =======================================
platforms:
- deviceName: Samsung Galaxy S22 Ultra
osVersion: "12.0"
platformName: android

# =======================
# Parallels per Platform
# =======================
parallelsPerPlatform: 1

# ==========================================
# BrowserStack Local (for the local test)
# ==========================================
browserstackLocal: true

source: gauge-appium-app-browserstack:sample-sdk:v1.0

# ======================
# Test Observability
# ======================
testObservability: true

# ===================
# Debugging features
# ===================
debug: true
networkLogs: true
103 changes: 103 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
plugins {
id 'java'
id 'org.gauge' version '1.8.2'
// BrowserStack Gradle SDK plugin (latest). Declared per the BrowserStack SDK convention.
id 'com.browserstack.gradle-sdk' version '3.1.6'
}

apply plugin: 'com.browserstack.gradle-sdk'

group 'com.browserstack'
version '1.0-SNAPSHOT'

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

repositories {
mavenCentral()
mavenLocal()
}

configurations.all {
resolutionStrategy {
eachDependency { details ->
if (details.requested.group == 'com.google.guava' && details.requested.name == 'guava') {
details.useVersion '32.1.2-jre'
details.because 'Align Guava across Selenium/Appium/Gauge for Java 11+'
}
}
}
}

dependencies {
implementation 'com.google.guava:guava:32.1.2-jre'
// gauge-java MUST match the installed Gauge Java runner plugin version. The BrowserStack Java SDK's
// gauge instrumentation is validated against gauge-java 0.12.0 (the reference pin); pair it with the
// matching Gauge `java` runner plugin (0.12.0). A newer gauge-java (1.0.1) trips SDK error SDK-TRA-006.
implementation 'com.thoughtworks.gauge:gauge-java:0.12.0'
implementation 'org.seleniumhq.selenium:selenium-java:4.13.0'
implementation 'io.appium:java-client:8.6.0'
implementation 'com.browserstack:browserstack-local-java:1.0.6'
implementation 'commons-io:commons-io:2.11.0'
testImplementation 'org.assertj:assertj-core:3.20.2'

// BrowserStack Java SDK — provides the -javaagent that instruments the Appium driver and
// injects app + device capabilities from browserstack.yml. The agent is attached to the
// Gauge Java *runner* JVM via env/default/java.properties (gauge_jvm_args). Resolving LATEST
// from Maven Central also makes the jar available under ~/.m2 / the Gradle cache so the
// -javaagent path can point at it. See README.md.
// NOTE: Gradle does not understand Maven's "LATEST" keyword — use "latest.release".
implementation 'com.browserstack:browserstack-java-sdk:latest.release'
}

// Convenience: copy the resolved BrowserStack Java SDK jar into build/agent so env/default/java.properties
// can reference a stable, version-independent path (build/agent/browserstack-java-sdk.jar).
task copyBrowserStackAgent(type: Copy) {
description = 'Stage the BrowserStack Java SDK jar for the -javaagent on the Gauge runner JVM.'
from {
configurations.runtimeClasspath.find { it.name ==~ /browserstack-java-sdk-.*\.jar/ }
}
into "$projectDir/build/agent"
rename { 'browserstack-java-sdk.jar' }
}

// Helper: build a Gauge-CLI run task.
// We invoke the Gauge CLI directly (rather than the org.gauge GaugeTask, which subclasses Gradle's
// `Test` task and is skipped as NO-SOURCE because there are no JUnit/TestNG classes). The Gauge Java
// runner classpath is provided explicitly via the gauge_custom_classpath env var, sourced from the
// project's compiled test classes + full runtime dependencies. The -javaagent is wired separately on
// the Gauge runner JVM via env/default/java.properties (gauge_jvm_args).
def gaugeRunTask = { String taskName, String specPath, String desc ->
tasks.register(taskName, Exec) {
group = 'verification'
description = desc
dependsOn 'testClasses', 'copyBrowserStackAgent'
workingDir projectDir
doFirst {
def cp = sourceSets.test.runtimeClasspath.files
.collect { it.absolutePath }.join(File.pathSeparator)
environment 'gauge_custom_classpath', cp
commandLine 'gauge', 'run', '--verbose', specPath
}
}
}

gaugeRunTask('runSampleTest', 'specs/bstack-specs/bstack-sample.spec',
'Run the WikipediaSample sample spec on BrowserStack App Automate.')
gaugeRunTask('runLocalTest', 'specs/bstack-specs/local-test.spec',
'Run the LocalSample local-connection spec on BrowserStack App Automate.')
gaugeRunTask('runTests', 'specs/bstack-specs',
'Run the full Gauge spec suite on BrowserStack App Automate.')

// Print the test runtime classpath (compiled test classes + all runtime deps) so the Gauge CLI can
// also be invoked manually: `export gauge_custom_classpath=$(cat build/gauge-classpath.txt); gauge run …`
task printGaugeClasspath {
dependsOn 'testClasses'
doLast {
def cp = sourceSets.test.runtimeClasspath.files.collect { it.absolutePath }.join(File.pathSeparator)
new File("$projectDir/build/gauge-classpath.txt").text = cp
println "WROTE_CLASSPATH"
}
}
Loading
Loading