This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Example project showing how to drive a remote Appium session on the TestingBot cloud device grid from Robot Framework to run native mobile app tests. It is intentionally small — one suite (test.robot) plus two Python helper libraries.
PYTHONPATH must include the repo root so Robot Framework can import the local Python libraries (AppOptions.py, TestingBot.py) as libraries named AppOptions / TestingBot.
pip install -r requirements.txt
export TB_KEY=<key>
export TB_SECRET=<secret>
PYTHONPATH=$PYTHONPATH:. robot \
--variable PLATFORM:Android \
--variable VERSION:14.0 \
--variable DEVICE:Pixel.* \
--variable APP:https://testingbot.com/appium/sample.apk \
test.robot
Run a single test case: add --test "Simple App Test" (or -t) to the robot invocation.
TB_KEY / TB_SECRET are read from os.environ inside TestingBot.py — do not pass them via --variable or embed them in the remote URL, or they will leak into log.html / output.xml. The four --variable args (PLATFORM, VERSION, DEVICE, APP) are required — the suite references them with no defaults. CI (.github/workflows/test-python.yml) injects TB_KEY/TB_SECRET via the job's env: block from GitHub secrets.
The suite is three pieces that plug together via Robot Framework's Library imports:
-
test.robot— declaresAppiumLibrary,AppOptions, andTestingBotas libraries.Test SetupcallsGet App Optionsto get an Appium platform-specific*Optionsobject (e.g.UiAutomator2Options/XCUITestOptions), attaches W3C + Appium capabilities and atb:optionsdict (TestingBot-specific metadata like build name) viaCall Method ... set_capability, then hands the configured options off toOpen Testingbot App. The suite never references credentials. -
AppOptions.py— factory returningUiAutomator2Options/XCUITestOptionsbased on thePLATFORMvariable. Keeping this in Python (rather than Robot keywords) is deliberate: the test needs a real Appium options object to callset_capabilityon. -
TestingBot.py— owns all TestingBot/credential concerns. ReadsTB_KEY/TB_SECRETfromos.environ, builds aselenium.webdriver.remote.client_config.ClientConfigwith basic auth, and creates the remote Appium driver viaappium.webdriver.Remote(...). It then registers the driver in the runningAppiumLibraryinstance's_cache— this both avoids URL-embedded credentials (which would be logged) and means subsequent AppiumLibrary keywords (Wait Until Page Contains Element,Click Element, …) just work.Report Testingbot Statusthen reaches into the runningAppiumLibraryinstance for the livesession_idand PUTs pass/fail toapi.testingbot.com/v1/tests/<session_id>. This only works while the app session is still open, which is why it runs beforeClose Applicationin the teardown.
Note: AppiumLibrary's own Open Application keyword also creates the driver, but only accepts capability kwargs and would require embedding credentials in the remote URL. Registering our own driver in appium._cache is the escape hatch that keeps credentials out of the URL and the logs.
When changing capabilities or adding platform-specific flags, edit test.robot (capabilities) or AppOptions.py (options object construction) — don't try to do it purely in Robot syntax. If you need to change how the driver is created or how auth is handled, that all lives in TestingBot.py:open_testingbot_app — keep credentials out of test.robot and out of Robot variables.
log.html, report.html, output.xml, .pabotsuitenames, and pabot_results/ are generated by robot/pabot runs and should not be committed or treated as source.