Appium UI Automation¶
Jumpstarter gives every UI-automation target the same Appium client interface, whatever its platform: physical devices, emulators and simulators, on Android or iOS, local or remote. A test asks the target for an Appium endpoint and opens WebDriver sessions on it; it does not change when the target does.
Why Appium¶
Appium is Jumpstarter’s default UI-automation framework because it suits how Jumpstarter is used:
One API on every platform. Appium speaks the W3C WebDriver protocol and hands each session to a platform driver, such as UiAutomator2 for Android or XCUITest for iOS, with further drivers for desktop and TV platforms. Tests use the same client calls whichever device they drive, so one suite covers every target, and every Jumpstarter target can offer the same interface.
Python support. Jumpstarter clients are written in Python, and the Appium project maintains an official Python client, Appium-Python-Client, built on Selenium’s Python bindings. It installs with
pip, works with pytest and other Python test runners, and accepts the configuration Jumpstarter hands it directly. Clients in other languages, such as Java, JavaScript, Ruby or .NET, can connect too.Nothing to install on the client. Appium and its platform drivers run on the exporter, next to the device, so clients need only a WebDriver client library: no Appium, Node.js or platform SDKs.
Open and established. Appium is open source under the Apache 2.0 licence, hosted by the OpenJS Foundation, and widely used, so existing Appium tests, tools and experience carry over.
How it works¶
Each target driver adds an appium child when the exporter configures one. The
exporter runs Appium for the lease, next to the device, and the client reaches it
over the lease through verified HTTPS. Clients need neither Appium nor the
device’s tools.
Target |
Driver |
Appium runs |
Configure |
|---|---|---|---|
Android device |
exporter |
|
|
Android emulator |
exporter |
|
|
iPhone or iPad on USB |
exporter |
|
|
iOS simulator |
exporter (macOS) |
|
|
iPhone or iPad in Xcode’s Device Hub |
this Mac |
|
Device Hub is the one case where Appium runs on the client: WebDriverAgent is reachable only inside the Mac’s Device Hub connection, so the Mac starts your installed Appium. It still yields the same target, so tests do not change.
Every target pins the session to its own device. Capabilities such as
appium:udid, the device set or the ADB server are fixed by the exporter and
replace whatever a client sends.
Write the test once¶
from appium import webdriver
from appium.options.common import AppiumOptions
from jumpstarter.common.utils import env
with env() as root, root.phone.appium.serve() as target:
options = AppiumOptions().load_capabilities(target.capabilities())
driver = webdriver.Remote(target.url, options=options, client_config=target.client_config())
try:
print(driver.page_source)
finally:
driver.quit()
root.phone is the target’s export name. For a simulator or emulator, power it
on first (root.simulator.power.on()); Appium waits for it to boot. For Device
Hub, replace root.phone.appium.serve() with
root.phone.devicehub_appium("WebDriverAgentRunner.xctestrun").
target.capabilities() holds what the target fixes, such as platformName,
appium:automationName and appium:udid. Add your own, for example
appium:bundleId or appium:appPackage, before opening a session.
If your client configuration allow-lists driver packages under drivers.allow,
add jumpstarter_driver_appium.*, or the appium child cannot load.
Install Appium-Python-Client to run
this example. For complete pytest suites that drive the built-in Settings app
through a local exporter, see python/examples/appium-android-emulator and
python/examples/appium-ios-simulator in the repository. Other WebDriver clients work too: use target.ssl_context() or
target.ca_file for the lease’s certificate authority, and keep certificate
verification enabled.
Applications¶
The exporter’s Appium cannot read files on your machine. Upload an application first, then pass the token it returns wherever Appium expects an application:
token = target.upload_app("build/app-debug.apk") # or an .ipa, or a .app bundle
options.set_capability("appium:app", token)
# or, inside a session; the argument is appPath on Android and app on iOS:
driver.execute_script("mobile: installApp", {"appPath": token})
driver.execute_script("mobile: installApp", {"app": token})
root.phone.appium.upload_app() does the same without serving Appium first.
Installed applications start with appium:bundleId (iOS) or
appium:appPackage (Android). A token works only on the target that issued it.
Device Hub sessions accept the same capabilities, with tokens from the Device
Hub target’s own upload_app(). Appium on your Mac cannot install applications
over Device Hub, so Jumpstarter installs them with xcrun devicectl before
Appium sees the request, resetting an installed copy as Appium does, and starts
an appium:app by its bundle identifier. Web views, MJPEG screen streaming and
mobile: listApps are not available over Device Hub; see
iosdevice.
Command line¶
Inside a jmp shell:
j phone appium # print url, ca_file and capabilities until Ctrl+C
j phone appium -- pytest tests/ui # run a command with the endpoint in its environment
j phone appium upload build/app.ipa # print an application token
The command receives JUMPSTARTER_APPIUM_URL, JUMPSTARTER_APPIUM_CAPABILITIES
(JSON), JUMPSTARTER_APPIUM_CA_FILE and Node’s NODE_EXTRA_CA_CERTS, so test
runners in other languages can connect too. For iOS targets the export is
usually ios (j ios appium), and for a simulator or emulator the appium
child sits next to power (j simulator appium).
Prepare the exporter¶
The exporter needs Node.js, Appium and the Appium driver for its targets:
Android: UiAutomator2, a JDK, and the Android SDK’s platform tools and build tools, with
JAVA_HOMEandANDROID_HOMEset in the exporter’s environment.iOS: XCUITest.
Jumpstarter does not bundle, download, build or sign Appium, its drivers or WebDriverAgent.
Containerized exporters pin these in their image. For example, on top of an exporter image:
FROM quay.io/jumpstarter-dev/jumpstarter:latest
RUN dnf install -y nodejs npm java-21-openjdk-headless && dnf clean all
# For Android targets, install the SDK's platform-tools and build-tools here, for
# example with sdkmanager, and set JAVA_HOME to the JDK.
ENV ANDROID_HOME=/opt/android-sdk
ENV APPIUM_HOME=/opt/appium/home
RUN npm install --prefix /opt/appium appium@3.8.0 && \
/opt/appium/node_modules/.bin/appium driver install uiautomator2@8.7.3 && \
/opt/appium/node_modules/.bin/appium driver install xcuitest@12.22.8
Then point the target’s appium block at that installation:
export:
phone:
type: jumpstarter_driver_adb.driver.AdbDevice
config:
usb_port: "1-4.2"
appium:
executable: /opt/appium/node_modules/.bin/appium
home: /opt/appium/home
macOS exporters for simulators install Appium and its XCUITest driver on the host in the same way. Each driver’s page lists its own options.
Security¶
A lease controls its device through Appium, and Appium runs inside the exporter. The Appium driver keeps it to the device:
Appium listens on exporter loopback only, with every Appium insecure feature denied, no plugins and a private home directory.
Capabilities that name exporter files, executables or URLs, the
proxycapability andremotePathuploads are rejected, so clients cannot make the exporter read its files or contact other hosts. Applications are accepted only as uploads.Target-specific capabilities that would reach beyond the device, such as starting emulators or another ADB host on Android, or erasing the simulator, are rejected or fixed off.
Leases still get full control of their device. Reset or re-provision devices between untrusted tenants, and keep exporters that serve untrusted tenants dedicated to that role.