This release is a pre-release and may not be stable for production use.
testmuai-appium-bindings
Appium runtime bindings for KaneAI v4 mobile exports.
Distribution name vs import name Install
testmuai-appium-bindings; importtestmu_appium.
Installation
pip install testmuai-appium-bindings
Quick start
A generated mobile test is a thin script over this runtime:
import testmu_appium
from testmu_appium import var, set_var
testmu_appium.configure(kane_run_v4=True, app_id="com.google.android.gm", platform="android")
@testmu_appium.test
def test(driver):
with testmu_appium.step("Tap the Compose button"):
testmu_appium.click(
driver,
selectors=[
{"strategy": "view_id", "selector": "com.google.android.gm:id/compose",
"score": 90, "isXPath": False},
{"strategy": "accessibility_id", "selector": "Compose",
"score": 80, "isXPath": False},
],
description="PRIMARY: Compose button HINTS: bottom-right, red FAB",
fallback_coordinates={"x_ratio": 0.8631, "y_ratio": 0.9012,
"orientation": "portrait", "window": [1080, 2340]},
)
if __name__ == "__main__":
testmu_appium.run(test)
The generated code is platform-portable: it carries selector data and semantic key
names only. Strategy compilation, keycode maps and picker machinery are runtime data
tables keyed off the configured platform.
Configuration
| Env var | Default | Purpose |
|---|---|---|
TESTMU_RUN_TARGET |
local |
local (Appium server) or cloud (LT mobile hub) |
APPIUM_URL |
http://127.0.0.1:4723 |
Local Appium server |
LT_HUB_URL |
https://mobile-hub.lambdatest.com/wd/hub |
LambdaTest mobile hub |
LT_USERNAME / LT_ACCESS_KEY |
— | LambdaTest credentials |
TESTMU_SMART |
1 |
Autoheal + AI-backed helpers |
TESTMU_AI_API_HOST |
https://kaneai-api.lambdatest.com/v16-server |
Autoheal / query endpoints |
TESTMU_ACTION_TIMEOUT_MS |
10000 |
Per-action find budget (default_action_timeout_ms) |
TESTMU_SETTLE_TIMEOUT_MS |
3000 |
Pre-action stability wait budget |
TESTMU_SCREENSHOT_SOURCE |
auto |
auto, mjpeg or appium (screenshot_source) |
TESTMU_MJPEG_PORT |
7813 |
Host port Appium forwards the MJPEG stream to (mjpeg_port) |
configure() raises on unknown keys — a generator emitting a key this binding
version does not understand fails at import instead of silently dropping it.
Screenshots
Perception screenshots come from the uiautomator2 server's MJPEG broadcaster where
it is reachable, and from driver.get_screenshot_as_png() otherwise. The session
asks Appium for the appium:mjpegServerPort forward on a local run target only —
a cloud session's forward lives on the device host, which 127.0.0.1 here does not
reach — and a stream that fails once is not tried again for the rest of the session.
Perception.screenshot_source names the path that served the frame.
screenshot_source="mjpeg" takes the stream on any run target and forbids the
Appium fallback; screenshot_source="appium" never opens the stream.
The generated call surface
The verb names and their parameters are a contract shared with the code
generator, pinned on this side by tests/test_public_surface.py
(BINDING_PUBLIC_SURFACE) and on the generator's side by its verb-table test.
Both tables carry the same rows and are edited together.
Verbs come in three shapes:
- element —
click,type,search,clear,select,scroll,scroll_until. Takeselectors; run settle → find → act → heal. - driver —
drag,navigate,keyevent,app_lifecycle,device_control,wait,smartui_screenshot,check_until_condition,verify_assertion,textual_query,vision_query,network_query,network_capture_query. Take the driver, never take selectors, never heal. - value —
evaluate_math,math,execute_api,execute_db. Pure; take no driver at all.
verify_assertion(tree=...) and evaluate_math(tree=...) accept the recorded
assertion_tree / mathmatic_tree the generator emits. assertion(...) and
math(expression=...) are the flat-argument forms behind them and stay
available for hand-written tests.
Network capture contracts
Generated artifacts should use network_capture_query(driver, contract=...) with
network.capture.v1: exactly schema_version, a selector (method, url,
zero-based occurrence, flow_id), and millisecond wait values. The provider is
runtime configuration only: TESTMU_NETWORK_CAPTURE_URL selects the stable
GET /v1/network/capture/flows, /flows/{id}, and /capabilities contract;
TESTMU_NETWORK_CAPTURE_PROVIDER=lambda-har adapts a legacy /har provider.
Without an explicit URL, a runtime that provides HOST_IP and PROXY_API_PORT
derives the legacy endpoint (environment first, then rd-details.env in the temp
directory), including authoring runs whose local run-target setting has not changed.
network_capture_capabilities() reports provider readiness. network_query(...)
remains a compatibility wrapper around the canonical contract.
evaluate_network_assertion(tree, contract_version="network.assert.v1") returns
a deterministic passed/failed/indeterminate result with evidence and matching flow
IDs. Failed or unavailable-body results raise NetworkAssertionError unless
TESTMU_SKIP_ASSERTION_FAILURE is truthy.
Perception API
The UI-tree parser is published for callers outside this package — a host runtime
driving its own mobile session, for instance. Import it from
testmu_appium.perception; the same names are re-exported from the package root.
from testmu_appium.perception import (
parse_tree, format_for_prompt, find_by_fingerprint, position_hint,
EDITABLE_CLASSES, ROLE_MAP,
)
The binding owns this parser. V16, healing, and generated-test runtime all consume
the public API above so document order and element identity have one definition.
testmu_appium._helpers._tree remains an implementation detail.
parse_tree(xml_str, screen_w, screen_h) -> list[dict]
Parses an Appium page-source XML document into a flat list of useful on-screen
nodes. A node is kept when it has a readable label, a resource id, or responds to
touch; pure layout wrappers are skipped and their children promoted. Nodes are
dropped when their bounds attribute is missing, zero-area, hidden, or outside
the screen_w x screen_h viewport. Entries are de-duplicated on
(role, name, center), first occurrence winning.
Every row carries interactive and depth. format_for_prompt filters to the
interactive subset and assigns its own dense display numbering, preserving the
visual-mode contract while text-mode consumers can reference informational rows.
Indices are 1-based. entry["index"] runs 1..len(result) over the returned
list with no gaps, and that index is what the autoheal endpoint's dom_index refers
to. Re-parsing a changed screen renumbers everything, so an index is only meaningful
against the parse it came from.
Each entry carries exactly these keys:
| Key | Type | Meaning |
|---|---|---|
index |
int |
1-based position in the returned list |
role |
str |
One of input, button, text, image, switch, checkbox, radio, slider, picker, dropdown, webview, scrollable, item |
name |
str |
The element's label: its own text, else content-desc, else hint. A clickable node with no label of its own borrows up to 3 labels from its non-interactive descendants, joined with " · ". Truncated to 80 characters. |
bounds |
tuple[int, int, int, int] |
(x1, y1, x2, y2) in device pixels |
center |
tuple[int, int] |
(cx, cy) in device pixels |
states |
list[str] |
Any of checked, disabled, focused, selected, password |
scrollable |
bool |
The node's scrollable attribute |
cls |
str |
Raw class attribute |
resource_id |
str |
Raw resource-id attribute, "" when absent |
content_desc |
str |
Raw content-desc attribute, "" when absent |
text |
str |
Raw text attribute, "" when absent |
hint |
str |
Raw hint attribute, "" when absent |
position |
str |
<top|middle|bottom>-<left|center|right>, from the centre against the viewport thirds |
screen_w / screen_h are the live window size in device pixels — the same units the
bounds attribute uses.
format_for_prompt(elements) -> str
Renders the list from parse_tree as one line per entry, in the form a reasoning
model reads: [<index>] <role> "<name>" [<states>] (<position>). Scrollable entries
get a line noting that content may exist beyond the viewport. Returns
"(no interactive elements detected — rely on the screenshot)" for an empty list.
find_by_fingerprint(elements, fp) -> dict | None
Re-finds a previously recorded element in a fresh parse_tree result. fp is a dict
that may carry resource_id, text, content_desc and name; they are tried in
that order, with resource_id + text preferred over resource_id alone. Returns
the matching entry, or None when nothing matches.
ROLE_MAP
The ordered (class-name suffix, role) pairs parse_tree uses to assign role,
matched by class.endswith(suffix), first match winning. A node matching no suffix
becomes scrollable when its scrollable attribute is set, else item.
Platform support
Android ships today. iOS is accepted as configuration (platform="ios") so its
arrival is a data event, but the iOS strategy column, keycode map and session-options
row are not shipped yet and raise UnsupportedOnPlatform.
Development
pip install -e ".[dev]"
pytest -v
Tests are hermetic — no live device, no live grid, no live HTTP.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file testmuai_appium_bindings-0.3.0b1.tar.gz.
File metadata
- Download URL: testmuai_appium_bindings-0.3.0b1.tar.gz
- Upload date:
- Size: 409.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb31c46ea44b3f0957df3dc1948d1f0a5895bc8195ec3726b5c0ef32f5ca1b32
|
|
| MD5 |
327893093a608cd1f75cc5ced44d95fc
|
|
| BLAKE2b-256 |
f12e0e3db7da9fe904b12893d4698546bd210e47381898b28bafd2027fb8b277
|
File details
Details for the file testmuai_appium_bindings-0.3.0b1-py3-none-any.whl.
File metadata
- Download URL: testmuai_appium_bindings-0.3.0b1-py3-none-any.whl
- Upload date:
- Size: 274.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c38380e9262a330d70d991e2cb26726314541e7fae2d28e176396bf35b5d7e35
|
|
| MD5 |
d8715ad7177a52f5eec8af1db5c17363
|
|
| BLAKE2b-256 |
2472ae7d8756857c3b500a6a10be405956cf2377660ff72ab50c143930a69a30
|