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
84 changes: 82 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,82 @@
# vanilla-java-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.
# Vanilla Java Appium with BrowserStack App Automate

Run a vanilla Java Appium (Android) test on real devices via
[BrowserStack App Automate](https://app-automate.browserstack.com/) using the
[BrowserStack Java SDK](https://www.browserstack.com/docs/app-automate/appium/getting-started/java).

The SDK is attached as a `-javaagent`, reads `browserstack.yml`, selects the
pre-uploaded app, starts the Appium session on the chosen device, and reports
results back to BrowserStack — your test code stays plain `io.appium:java-client`
driving JUnit 5.

## Prerequisites

- A [BrowserStack](https://www.browserstack.com/) account (username + access key).
- JDK 8+ and Maven 3.6+.

## Setup

```bash
git clone <this-repo>
cd vanilla-java-appium/android
mvn -DskipTests compile
```

Configure credentials either in `browserstack.yml` (`userName` / `accessKey`)
or as environment variables:

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

The sample app is already referenced in `android/browserstack.yml` as a
pre-uploaded `bs://` app id (WikipediaSample.apk). To use your own build, set
`app:` to a local `.apk` path and the SDK will upload it for you.

## Run Sample Test

```bash
cd android
mvn test -Dtest=BStackSampleTest
```

The `-javaagent` is wired into the Maven Surefire plugin's `argLine` (the jar
path resolves from `~/.m2` via the `maven-dependency-plugin` `properties` goal),
so a plain `mvn test` attaches the SDK automatically. The sample test:

1. taps **Search Wikipedia** (accessibility id),
2. types `BrowserStack` into the search field, and
3. asserts the results list rendered at least one entry.

## Run Local Test

`BStackLocalTest` drives `LocalSample.apk` (pkg
`com.example.android.basicnetworking`) and asserts the in-app network check
reports **Up and running**, proving the BrowserStack Local tunnel is connected.
It is `@Disabled` by default because this `android/` dir ships wired to the
Wikipedia sample app. To run it:

1. point `browserstack.yml` `app:` at `LocalSample.apk`,
2. set `browserstackLocal: true`,
3. remove the `@Disabled` annotation, then:

```bash
cd android
mvn test -Dtest=BStackLocalTest
```

## Notes / Dashboard

- Watch sessions live and review video/logs at
[app-automate.browserstack.com](https://app-automate.browserstack.com/).
- `testObservability: true` also surfaces this build in
[BrowserStack Test Observability](https://observability.browserstack.com/).
- The SDK dependency `com.browserstack:browserstack-java-sdk` uses `LATEST`,
matching the published-sample convention (resolved to `1.59.7` at validation
time).
- `io.appium:java-client` is pinned to `8.2.1` with `selenium-java` `4.5.0`.
Newer java-client (8.6.0) trips the SDK 1.59.7 javaagent on an empty/injected
capability set (`IllegalArgumentException: Capabilities must be set`, session
never starts); the 8.2.1 + Selenium 4.5.0 combo is the known-good pairing and
passes a live device session.
7 changes: 7 additions & 0 deletions android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
target/
.idea/
*.iml
log/
local.log
*.log
.DS_Store
50 changes: 50 additions & 0 deletions android/browserstack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# =============================
# 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-vanilla-java-appium
buildIdentifier: '#${BUILD_NUMBER}'
# `framework` lets the Java SDK instrument the JUnit 5 runner so it can report
# per-test context (name, status) to BrowserStack. Unlike the Node App Automate
# samples (where `framework` is injected into bstack:options and the Appium hub
# rejects it), the Java SDK reports test context out-of-band via the -javaagent
# and does NOT push `framework` into the W3C session caps, so it is safe here.
framework: junit5

# Set `app` to the pre-uploaded BrowserStack app id (bs://...) or a local path.
app: bs://92d48b416632f2b1734259565ceab61b05ad0b24

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

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

source: vanilla-java-appium-app-browserstack:sample-sdk:v1.0

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

# ===================
# Debugging features
# ===================
debug: true
networkLogs: true
consoleLogs: errors
95 changes: 95 additions & 0 deletions android/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.browserstack</groupId>
<artifactId>vanilla-java-appium</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>vanilla-java-appium</name>
<url>https://www.github.com/browserstack/vanilla-java-appium-app-browserstack</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.jupiter.version>5.10.2</junit.jupiter.version>
</properties>

<dependencies>
<!-- Selenium 4.5.0 — the version Appium java-client 8.2.1 was built
against (still ships org.openqa.selenium.Rotatable, which the
Appium AndroidDriver references). -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.5.0</version>
</dependency>

<!-- Appium Java client (drives the Android session on the BrowserStack hub) -->
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>8.2.1</version>
</dependency>

<!-- JUnit 5 (Jupiter) — the test runner the SDK instruments. -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>

<!-- BrowserStack Java SDK: instruments the Appium driver + JUnit 5 runner
via the -javaagent (wired into surefire's argLine below). -->
<dependency>
<groupId>com.browserstack</groupId>
<artifactId>browserstack-java-sdk</artifactId>
<version>LATEST</version>
<scope>compile</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>

<!-- Exposes ${com.browserstack:browserstack-java-sdk:jar} = the absolute
path to the SDK jar resolved into ~/.m2, used by the -javaagent below. -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>getClasspathFilenames</id>
<goals>
<goal>properties</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<!-- Attach the BrowserStack Java SDK as a -javaagent so it can
instrument the Appium driver and report test results. -->
<argLine>-javaagent:${com.browserstack:browserstack-java-sdk:jar}</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
82 changes: 82 additions & 0 deletions android/src/test/java/com/browserstack/BStackLocalTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.browserstack;

import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Disabled;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.net.URL;
import java.time.Duration;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Local App Automate test against the LocalSample.apk app
* (pkg com.example.android.basicnetworking).
*
* Proves the BrowserStack Local tunnel is connected: triggers the in-app network
* test action and asserts the app reports it is "Up and running".
*
* Requires browserstackLocal: true and the LocalSample.apk app in browserstack.yml.
* Disabled by default because this android/ dir is wired to the Wikipedia sample
* app + a single platform; enable it once you point browserstack.yml at
* LocalSample.apk and set browserstackLocal: true.
*/
@Disabled("Enable after configuring browserstack.yml with LocalSample.apk + browserstackLocal: true")
public class BStackLocalTest {

private AndroidDriver driver;

@BeforeEach
public void setUp() throws Exception {
String userName = System.getenv("BROWSERSTACK_USERNAME");
String accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY");

// Minimal capabilities — the SDK injects the app + device caps from
// browserstack.yml on top. platformName satisfies the Appium java-client
// 8.x W3C validation (it rejects a fully-empty capability set).
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("platformName", "android");

driver = new AndroidDriver(
new URL("http://" + userName + ":" + accessKey + "@hub.browserstack.com/wd/hub"),
capabilities);
}

@Test
public void localTunnelUpAndRunning() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));

// Trigger the in-app network test action.
wait.until(ExpectedConditions.elementToBeClickable(
AppiumBy.id("com.example.android.basicnetworking:id/test_action"))).click();

wait.until(ExpectedConditions.presenceOfElementLocated(
AppiumBy.className("android.widget.TextView")));

// Assert a TextView reports the tunnel is up.
List<WebElement> textViews = driver.findElements(
AppiumBy.className("android.widget.TextView"));

boolean upAndRunning = textViews.stream()
.map(WebElement::getText)
.filter(t -> t != null)
.anyMatch(t -> t.contains("Up and running"));

assertTrue(upAndRunning, "Expected 'Up and running' text confirming the Local tunnel");
}

@AfterEach
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
Loading
Loading