Skip to content

Commit 1ab3c82

Browse files
Add vanilla-java-appium App Automate sample (Appium, Android)
1 parent 4b8321e commit 1ab3c82

9 files changed

Lines changed: 394 additions & 59 deletions

File tree

.github/workflows/Semgrep.yml

Lines changed: 0 additions & 49 deletions
This file was deleted.

.npmrc

Lines changed: 0 additions & 7 deletions
This file was deleted.

CODEOWNERS

Lines changed: 0 additions & 1 deletion
This file was deleted.

README.md

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,82 @@
1-
# vanilla-java-appium-app-browserstack
2-
We require the following new public repositories under the browserstack GitHub organization to host customer-facing sample projects for the BrowserStack SDK.
1+
# Vanilla Java Appium with BrowserStack App Automate
2+
3+
Run a vanilla Java Appium (Android) test on real devices via
4+
[BrowserStack App Automate](https://app-automate.browserstack.com/) using the
5+
[BrowserStack Java SDK](https://www.browserstack.com/docs/app-automate/appium/getting-started/java).
6+
7+
The SDK is attached as a `-javaagent`, reads `browserstack.yml`, selects the
8+
pre-uploaded app, starts the Appium session on the chosen device, and reports
9+
results back to BrowserStack — your test code stays plain `io.appium:java-client`
10+
driving JUnit 5.
11+
12+
## Prerequisites
13+
14+
- A [BrowserStack](https://www.browserstack.com/) account (username + access key).
15+
- JDK 8+ and Maven 3.6+.
16+
17+
## Setup
18+
19+
```bash
20+
git clone <this-repo>
21+
cd vanilla-java-appium/android
22+
mvn -DskipTests compile
23+
```
24+
25+
Configure credentials either in `browserstack.yml` (`userName` / `accessKey`)
26+
or as environment variables:
27+
28+
```bash
29+
export BROWSERSTACK_USERNAME="YOUR_USERNAME"
30+
export BROWSERSTACK_ACCESS_KEY="YOUR_ACCESS_KEY"
31+
```
32+
33+
The sample app is already referenced in `android/browserstack.yml` as a
34+
pre-uploaded `bs://` app id (WikipediaSample.apk). To use your own build, set
35+
`app:` to a local `.apk` path and the SDK will upload it for you.
36+
37+
## Run Sample Test
38+
39+
```bash
40+
cd android
41+
mvn test -Dtest=BStackSampleTest
42+
```
43+
44+
The `-javaagent` is wired into the Maven Surefire plugin's `argLine` (the jar
45+
path resolves from `~/.m2` via the `maven-dependency-plugin` `properties` goal),
46+
so a plain `mvn test` attaches the SDK automatically. The sample test:
47+
48+
1. taps **Search Wikipedia** (accessibility id),
49+
2. types `BrowserStack` into the search field, and
50+
3. asserts the results list rendered at least one entry.
51+
52+
## Run Local Test
53+
54+
`BStackLocalTest` drives `LocalSample.apk` (pkg
55+
`com.example.android.basicnetworking`) and asserts the in-app network check
56+
reports **Up and running**, proving the BrowserStack Local tunnel is connected.
57+
It is `@Disabled` by default because this `android/` dir ships wired to the
58+
Wikipedia sample app. To run it:
59+
60+
1. point `browserstack.yml` `app:` at `LocalSample.apk`,
61+
2. set `browserstackLocal: true`,
62+
3. remove the `@Disabled` annotation, then:
63+
64+
```bash
65+
cd android
66+
mvn test -Dtest=BStackLocalTest
67+
```
68+
69+
## Notes / Dashboard
70+
71+
- Watch sessions live and review video/logs at
72+
[app-automate.browserstack.com](https://app-automate.browserstack.com/).
73+
- `testObservability: true` also surfaces this build in
74+
[BrowserStack Test Observability](https://observability.browserstack.com/).
75+
- The SDK dependency `com.browserstack:browserstack-java-sdk` uses `LATEST`,
76+
matching the published-sample convention (resolved to `1.59.7` at validation
77+
time).
78+
- `io.appium:java-client` is pinned to `8.2.1` with `selenium-java` `4.5.0`.
79+
Newer java-client (8.6.0) trips the SDK 1.59.7 javaagent on an empty/injected
80+
capability set (`IllegalArgumentException: Capabilities must be set`, session
81+
never starts); the 8.2.1 + Selenium 4.5.0 combo is the known-good pairing and
82+
passes a live device session.

android/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
target/
2+
.idea/
3+
*.iml
4+
log/
5+
local.log
6+
*.log
7+
.DS_Store

android/browserstack.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# =============================
2+
# Set BrowserStack Credentials
3+
# =============================
4+
# Add your BrowserStack userName and accessKey here or set BROWSERSTACK_USERNAME and
5+
# BROWSERSTACK_ACCESS_KEY as env variables
6+
userName: YOUR_USERNAME
7+
accessKey: YOUR_ACCESS_KEY
8+
9+
# ======================
10+
# BrowserStack Reporting
11+
# ======================
12+
projectName: BrowserStack Samples
13+
buildName: appauto-vanilla-java-appium
14+
buildIdentifier: '#${BUILD_NUMBER}'
15+
# `framework` lets the Java SDK instrument the JUnit 5 runner so it can report
16+
# per-test context (name, status) to BrowserStack. Unlike the Node App Automate
17+
# samples (where `framework` is injected into bstack:options and the Appium hub
18+
# rejects it), the Java SDK reports test context out-of-band via the -javaagent
19+
# and does NOT push `framework` into the W3C session caps, so it is safe here.
20+
framework: junit5
21+
22+
# Set `app` to the pre-uploaded BrowserStack app id (bs://...) or a local path.
23+
app: bs://92d48b416632f2b1734259565ceab61b05ad0b24
24+
25+
# =======================================
26+
# Platforms (Devices to test)
27+
# =======================================
28+
platforms:
29+
- deviceName: Samsung Galaxy S22 Ultra
30+
osVersion: "12.0"
31+
platformName: android
32+
33+
# =======================
34+
# Parallels per Platform
35+
# =======================
36+
parallelsPerPlatform: 1
37+
38+
source: vanilla-java-appium-app-browserstack:sample-sdk:v1.0
39+
40+
# ======================
41+
# Test Observability
42+
# ======================
43+
testObservability: true
44+
45+
# ===================
46+
# Debugging features
47+
# ===================
48+
debug: true
49+
networkLogs: true
50+
consoleLogs: errors

android/pom.xml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>com.browserstack</groupId>
8+
<artifactId>vanilla-java-appium</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
<packaging>jar</packaging>
11+
12+
<name>vanilla-java-appium</name>
13+
<url>https://www.github.com/browserstack/vanilla-java-appium-app-browserstack</url>
14+
15+
<properties>
16+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
17+
<maven.compiler.source>1.8</maven.compiler.source>
18+
<maven.compiler.target>1.8</maven.compiler.target>
19+
<junit.jupiter.version>5.10.2</junit.jupiter.version>
20+
</properties>
21+
22+
<dependencies>
23+
<!-- Selenium 4.5.0 — the version Appium java-client 8.2.1 was built
24+
against (still ships org.openqa.selenium.Rotatable, which the
25+
Appium AndroidDriver references). -->
26+
<dependency>
27+
<groupId>org.seleniumhq.selenium</groupId>
28+
<artifactId>selenium-java</artifactId>
29+
<version>4.5.0</version>
30+
</dependency>
31+
32+
<!-- Appium Java client (drives the Android session on the BrowserStack hub) -->
33+
<dependency>
34+
<groupId>io.appium</groupId>
35+
<artifactId>java-client</artifactId>
36+
<version>8.2.1</version>
37+
</dependency>
38+
39+
<!-- JUnit 5 (Jupiter) — the test runner the SDK instruments. -->
40+
<dependency>
41+
<groupId>org.junit.jupiter</groupId>
42+
<artifactId>junit-jupiter</artifactId>
43+
<version>${junit.jupiter.version}</version>
44+
<scope>test</scope>
45+
</dependency>
46+
47+
<!-- BrowserStack Java SDK: instruments the Appium driver + JUnit 5 runner
48+
via the -javaagent (wired into surefire's argLine below). -->
49+
<dependency>
50+
<groupId>com.browserstack</groupId>
51+
<artifactId>browserstack-java-sdk</artifactId>
52+
<version>LATEST</version>
53+
<scope>compile</scope>
54+
</dependency>
55+
</dependencies>
56+
57+
<build>
58+
<plugins>
59+
<plugin>
60+
<groupId>org.apache.maven.plugins</groupId>
61+
<artifactId>maven-compiler-plugin</artifactId>
62+
<version>3.8.1</version>
63+
<configuration>
64+
<source>${maven.compiler.source}</source>
65+
<target>${maven.compiler.target}</target>
66+
</configuration>
67+
</plugin>
68+
69+
<!-- Exposes ${com.browserstack:browserstack-java-sdk:jar} = the absolute
70+
path to the SDK jar resolved into ~/.m2, used by the -javaagent below. -->
71+
<plugin>
72+
<artifactId>maven-dependency-plugin</artifactId>
73+
<executions>
74+
<execution>
75+
<id>getClasspathFilenames</id>
76+
<goals>
77+
<goal>properties</goal>
78+
</goals>
79+
</execution>
80+
</executions>
81+
</plugin>
82+
83+
<plugin>
84+
<groupId>org.apache.maven.plugins</groupId>
85+
<artifactId>maven-surefire-plugin</artifactId>
86+
<version>3.2.5</version>
87+
<configuration>
88+
<!-- Attach the BrowserStack Java SDK as a -javaagent so it can
89+
instrument the Appium driver and report test results. -->
90+
<argLine>-javaagent:${com.browserstack:browserstack-java-sdk:jar}</argLine>
91+
</configuration>
92+
</plugin>
93+
</plugins>
94+
</build>
95+
</project>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.browserstack;
2+
3+
import io.appium.java_client.AppiumBy;
4+
import io.appium.java_client.android.AndroidDriver;
5+
import org.junit.jupiter.api.AfterEach;
6+
import org.junit.jupiter.api.BeforeEach;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.Disabled;
9+
import org.openqa.selenium.WebElement;
10+
import org.openqa.selenium.remote.DesiredCapabilities;
11+
import org.openqa.selenium.support.ui.ExpectedConditions;
12+
import org.openqa.selenium.support.ui.WebDriverWait;
13+
14+
import java.net.URL;
15+
import java.time.Duration;
16+
import java.util.List;
17+
18+
import static org.junit.jupiter.api.Assertions.assertTrue;
19+
20+
/**
21+
* Local App Automate test against the LocalSample.apk app
22+
* (pkg com.example.android.basicnetworking).
23+
*
24+
* Proves the BrowserStack Local tunnel is connected: triggers the in-app network
25+
* test action and asserts the app reports it is "Up and running".
26+
*
27+
* Requires browserstackLocal: true and the LocalSample.apk app in browserstack.yml.
28+
* Disabled by default because this android/ dir is wired to the Wikipedia sample
29+
* app + a single platform; enable it once you point browserstack.yml at
30+
* LocalSample.apk and set browserstackLocal: true.
31+
*/
32+
@Disabled("Enable after configuring browserstack.yml with LocalSample.apk + browserstackLocal: true")
33+
public class BStackLocalTest {
34+
35+
private AndroidDriver driver;
36+
37+
@BeforeEach
38+
public void setUp() throws Exception {
39+
String userName = System.getenv("BROWSERSTACK_USERNAME");
40+
String accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY");
41+
42+
// Minimal capabilities — the SDK injects the app + device caps from
43+
// browserstack.yml on top. platformName satisfies the Appium java-client
44+
// 8.x W3C validation (it rejects a fully-empty capability set).
45+
DesiredCapabilities capabilities = new DesiredCapabilities();
46+
capabilities.setCapability("platformName", "android");
47+
48+
driver = new AndroidDriver(
49+
new URL("http://" + userName + ":" + accessKey + "@hub.browserstack.com/wd/hub"),
50+
capabilities);
51+
}
52+
53+
@Test
54+
public void localTunnelUpAndRunning() {
55+
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
56+
57+
// Trigger the in-app network test action.
58+
wait.until(ExpectedConditions.elementToBeClickable(
59+
AppiumBy.id("com.example.android.basicnetworking:id/test_action"))).click();
60+
61+
wait.until(ExpectedConditions.presenceOfElementLocated(
62+
AppiumBy.className("android.widget.TextView")));
63+
64+
// Assert a TextView reports the tunnel is up.
65+
List<WebElement> textViews = driver.findElements(
66+
AppiumBy.className("android.widget.TextView"));
67+
68+
boolean upAndRunning = textViews.stream()
69+
.map(WebElement::getText)
70+
.filter(t -> t != null)
71+
.anyMatch(t -> t.contains("Up and running"));
72+
73+
assertTrue(upAndRunning, "Expected 'Up and running' text confirming the Local tunnel");
74+
}
75+
76+
@AfterEach
77+
public void tearDown() {
78+
if (driver != null) {
79+
driver.quit();
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)