mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 19:47:12 +01:00
Shared across every repo with an Android surface (screen-locker's workout_app, todo, dufs-cloud/app). Works against a plain release APK on a physical device over adb — no root, no emulator, no Flutter debug connection, no app changes. Driving an app by tapping pixel positions read off a screenshot fails in ways that look like success. All observed in one session (2026-08-10) verifying workout_app's Firebase restore: * A tap aimed at a password field landed with the field unfocused. The typed password went nowhere and NOTHING errored — the next screenshot just showed an empty box. type_into/type_into_field now re-read the field and raise. * Opening the soft keyboard moved a field from y=1572 to y=1319, so a coordinate captured one step earlier was already wrong. Every action re-resolves the element immediately before acting. * A widget behind the keyboard reports its LAID-OUT position (1549) not its real one (1939) — a 390px error that taps a letter key and reports success. keyboard_is_up() distinguishes "absent" from "behind the keyboard", and tap closes the keyboard first. * KEYCODE_BACK is a route pop in Flutter, not a keyboard dismiss: it navigated out of Settings and discarded typed credentials, twice. dismiss_keyboard tries ESCAPE then BACK and verifies via dumpsys input_method. * `input text` inserts at the cursor, so typing over a filled field produced "canary@example.com321krzychu@gmail.com". Fields are cleared first. * uiautomator returns ONLY the focused EditText nodes while a Flutter text field has focus, and stays that way across retries — so a naive retry loop never converges. Partial trees are retried and the best result kept. * An EMPTY EditText has no text, content-desc or resource-id, so a "must be labelled" filter drops the very field you need to fill in. Editable nodes are kept regardless and addressable by position. Ambiguity is an error rather than a silent first-match: `tap "Back"` correctly refuses when "OFFLINE BACKUP" is also on screen. Its own test suite caught a real bug in _escape: backslash sat inside the escape loop, so "&" became "\\&" and a password containing shell metacharacters would have been typed wrong. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JupKvZCNKRmEeRQY3G1unG
49 lines
2.0 KiB
Python
49 lines
2.0 KiB
Python
"""Element-targeted Android UI automation over adb, for agents and scripts.
|
|
|
|
Drives a real device by *identity* (text, content-desc, resource-id) rather
|
|
than by pixel coordinates read off a screenshot. Coordinate tapping breaks in
|
|
three ways this package exists to remove, all of them observed on 2026-08-10
|
|
while verifying the workout app's Firebase restore:
|
|
|
|
* **A mis-tap is silent.** A tap at ``(540, 1705)`` aimed at a password field
|
|
landed with the field unfocused; the typed password went nowhere and nothing
|
|
errored. :func:`~python_pkg.android_ui.driver.AndroidUi.type_into` verifies
|
|
the field's text actually changed and raises when it did not.
|
|
* **Layout shifts invalidate coordinates.** Opening the soft keyboard moved a
|
|
field from y=1572 to y=1319, so a coordinate captured one step earlier was
|
|
already wrong. Every action re-reads the tree immediately before acting.
|
|
* **Screenshots are expensive and unassertable.** A full-page PNG per step
|
|
costs far more than the element list it stands in for, and cannot be
|
|
grepped. :meth:`~python_pkg.android_ui.driver.AndroidUi.dump` returns text.
|
|
|
|
Two device quirks are handled rather than documented, because both produced
|
|
false results before they were understood:
|
|
|
|
* ``uiautomator dump`` returns ONLY the ``EditText`` nodes while a Flutter text
|
|
field holds focus — the button you are about to tap is simply missing, and it
|
|
stays missing across retries, so a naive retry loop never converges.
|
|
* ``KEYCODE_BACK`` is treated by Flutter as a route pop, not a keyboard
|
|
dismiss: it navigated out of Settings and discarded typed input, twice.
|
|
|
|
See :mod:`python_pkg.android_ui.driver` for the API and
|
|
:mod:`python_pkg.android_ui.cli` for the command line.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from python_pkg.android_ui.driver import (
|
|
AmbiguousElementError,
|
|
AndroidUi,
|
|
ElementNotFoundError,
|
|
UiAutomationError,
|
|
UiElement,
|
|
)
|
|
|
|
__all__ = [
|
|
"AmbiguousElementError",
|
|
"AndroidUi",
|
|
"ElementNotFoundError",
|
|
"UiAutomationError",
|
|
"UiElement",
|
|
]
|