# GUI automation notes (Android wallet) Canonical notes for **graphical** Android wallet tests against GOA / stage. --- ## Minimal variant (implemented first) — status 2026-07 This is the **minimal vanilla** path we actually shipped; full dual-platform GUI is planned via flags (below), not required to use these scripts today. | Piece | Status | Notes | |-------|--------|--------| | F-Droid published APK install | **done** | `net.taler.wallet.fdroid` 1.6.1 / 854 | | Source build + same smoke | **done** | `:wallet:assembleFdroidDebug` → `net.taler.wallet.fdroid.debug` | | Default source branch | **done** | `dev/hernani-inference/fix-bank-withdraw-auto-exchange` (minimal GOA exchange auto-add) | | Deep-link entry (≡ QR) | **done** | `adb` `VIEW` `taler://withdraw/…` / `taler://pay…` | | Explorer API mint (chain) | **done** | `run-goa-gui-chain.sh` (GOA + stage) | | UI taps via uiautomator | **done** | best-effort Confirm/ToS/Pay; ANR → prefer **Wait** | | Screenshots / logcat artifacts | **done** | under `out*`, `out-gui-chain/` (gitignored) | | Host CLI e2e settlement | **separate** | `taler-monitoring.sh e2e` / `ladder` (not Android UI) | | Reliable unattended Confirm on 4 GiB Linux host | **limited** | System UI ANR; use phone or more RAM | | iOS GUI | **not in this tree** | see taler-ios `dev/hernani-inference/gui-workflows` | | macOS full dual AVD helpers | **upstream** | taler-android `gui-workflows` (Homebrew); ported ideas here for Linux | **Ground rule:** bring it to run; app diffs only if needed and **minimal**; prefer extending existing `dev/hernani-inference/*` branches over new trees. ### Minimalvariante: was geht / was nicht Einschränkungen der **aktuell implementierten** Minimalvariante (deep-link + uiautomator best-effort). Kein voller Dual-Platform-GUI-Stack, kein Ersatz für Host-CLI-Settlement. #### Geht (grün) | Fähigkeit | Wie / Hinweis | |-----------|----------------| | APK installieren | F-Droid `net.taler.wallet.fdroid` **oder** Source `assembleFdroidDebug` (`.debug`) | | GOA-Exchange ohne manuelles Add | Source-Build von `fix-bank-withdraw-auto-exchange` (Default in `run-android-build-and-smoke.sh`) | | Withdraw/Pay **starten** | `adb` deep-link `taler://withdraw/…` / `taler://pay…` (Ersatz für QR-Scan) | | Light UI-Drive | Confirm / Accept / Pay-Taps über Label-Suche (`lib_ui.py`, gui-smoke, chain) | | Multi-Runden-Taps | `run-android-gui-smoke.sh` / `run-goa-gui-chain.sh` (best-effort) | | Explorer-Mint (Kette) | `run-goa-gui-chain.sh` + `EXP_PW_FILE` (GOA + stage) | | Stack-Wahl | `STACK=goa` / `stage` (Bank/Merchant-URLs) | | Artefakte | Screenshots, UI-XML, logcat unter `out*` / `out-gui-chain/` (gitignored) | | Hybrid-Smoke | Install + Intent + leichte Taps: `run-android-pay-smoke.sh` | | Settlement **nachweisen** | **Host-seitig**: `taler-monitoring.sh e2e` / `ladder` (nicht Android-UI) | | Flags abschalten | `AUTO_ANDROID=0` skip; `AUTO_GUI=0` → nur deep-link/pay-smoke | #### Geht **nicht** oder nur eingeschränkt (rot / gelb) | Beschränkung | Folge / Workaround | |--------------|--------------------| | **Unattended Confirm → settled im Emulator (~4 GiB Host)** | Häufig **System UI ANR**; Taps bleiben hängen. Prefer **Wait** auf ANR-Dialog, echtes Gerät, oder Host mit mehr RAM. Kein zuverlässiges grünes E2E nur über GUI auf dem schwachen Emulator. | | **Vollständige Wallet-Settlement-Assertion in der GUI** | Minimalvariante prüft **nicht** „Balance final / transfer_done“ in der App-Oberfläche. Proof bleibt CLI/e2e. | | **Kamera / physischer QR-Scan** | Absichtlich nicht automatisiert; deep-link ist der Shortcut. Landing-QR-Pfade sind **HTTP**-Thema, nicht Android-GUI. | | **Browser-Shop-Checkout-UI** | Kein Chromium/WebView-Drive; Pay-URI kommt per Template-`POST` + Intent. | | **Volles ToS-Scroll / alle Dialoge** | Best-effort Accept-Tap; lange ToS oder unerwartete Sheets können hängen bleiben. | | **Published F-Droid allein auf GOA** | Kann Exchange-Add / Spinner-Probleme zeigen → **fix-branch-APK** bauen; bei Spinner-Hang optional `fix-withdraw-spinner-fallback`. | | **iOS-GUI** | Nicht in diesem Tree; siehe taler-ios `gui-workflows`. | | **macOS dual AVD / Homebrew-Helpers** | Upstream `gui-workflows`; hier nur Linux-taugliche Port-Ideen. | | **`AUTO_*` in jedem Script erzwungen** | Design + teilweise verdrahtet; Wrapper-Checkliste noch offen. | | **`run-until-done` / CLI-Wallet in GUI-Skripten** | Gehört zu CLI-Automation, nicht GUI; siehe `CLI-AUTOMATION-NOTES.md`. | | **CI grün = „User hat bezahlt“** | Smoke = Intent + best-effort UI. Wirtschaftlicher Erfolg = Host-e2e / ladder / Explorer. | #### Erwartetes Ergebnis pro Schicht | Schicht | Erfolgskriterium Minimalvariante | |---------|----------------------------------| | A Install | APK installed, App startet | | B Deep-link | Intent delivered, Wallet öffnet Withdraw/Pay-Flow | | C GUI taps | Dump+Tap ohne Crash; ANR ggf. mit Wait; **kein** Garant für „Paid“ | | D Evidence | Artefakte geschrieben | | Settlement | **Außerhalb** GUI: Host e2e/ladder | #### Wann Minimalvariante reicht - Smoke nach APK-/Branch-Build (Intent kommt an, App crasht nicht). - Manuelle Nacharbeit am Gerät nach vorbereiteten URIs. - GOA/stage **Erreichbarkeit** von Landing/Template + Wallet-Einstieg. #### Wann nicht reicht → nächster Schritt - Unattended grünes Pay-E2E auf dem Emulator → mehr RAM / physisches Gerät, ggf. `fix-withdraw-spinner-fallback`. - Beweis „Münzen da / Merchant paid“ → CLI e2e/ladder, nicht GUI-smoke. - iOS / Dual-Platform → andere Repos/Flags, nicht diese Skripte. ```bash cd scripts/taler-monitoring/android-test # Minimal hybrid (install + deep-link + light taps) STACK=goa ./run-android-pay-smoke.sh # GUI multi-round taps STACK=goa ./run-android-gui-smoke.sh STACK=stage ./run-android-gui-smoke.sh # Explorer mint + multi withdraw/pay chain (from gui-workflows) EXP_PW_FILE=$HOME/src/koopa/koopa-admin-secrets/koopa/host-root/taler-bank/bank-explorer-password.txt \ STACK=goa PKG=net.taler.wallet.fdroid.debug \ ./run-goa-gui-chain.sh # Build fix-branch APK then smoke (GUI=1 for gui smoke) ./run-android-build-and-smoke.sh GUI=1 ./run-android-build-and-smoke.sh ``` --- ## Platform capability flags (planned + defaults) Automation can run on hosts that support **Android only**, **GUI tooling only** (conceptual), or **both**. Flags keep that explicit for CI and laptops. ### Proposed env flags | Variable | Values | Meaning | |----------|--------|---------| | `AUTO_ANDROID` | `0` / `1` | Run Android wallet automation (adb, APK, emulator/device) | | `AUTO_GUI` | `0` / `1` | Drive **graphical** UI (uiautomator taps, multi-round). If `0`, deep-link/CLI-only install smoke. | | `AUTO_PLATFORM` | `auto` / `linux` / `macos` / `ios` | Host family (optional override) | | `EMULATOR_HEADLESS` | **`1` (default)** / `0` | No host window (`-no-window`). Server/CI-safe. | | `WINDOWED` | `0` / `1` | Shortcut: `WINDOWED=1` ⇒ show emulator window (`EMULATOR_HEADLESS=0`) | | `EMULATOR_GPU` | default **`swiftshader_indirect`** | Guest GLES still runs headless (layout/taps/screenshots). Override `host` only if windowed + real GPU. | | `AUTO_START_EMULATOR` | **`1` (default)** / `0` | If no adb device, start AVD via `start-android-emulator.sh --wait` | --- ## Headless mode (default) **Default for all GUI / pay smokes:** no window on the host. Opening an emulator GUI on servers (or headless CI) is wrong; these scripts therefore start the AVD with **`-no-window`** and still keep **graphics logic** in the guest: | Piece | Headless default | |-------|------------------| | Host window | **off** (`-no-window`, `QT_QPA_PLATFORM=offscreen`, no `DISPLAY`) | | Guest GLES | **on** via **SwiftShader** (`-gpu swiftshader_indirect`) | | UI automation | **on** — `uiautomator` dump/tap + `screencap` (no X11 needed) | | Audio / boot anim | off (`-no-audio -no-boot-anim`) | ```bash # explicit (same as default) ./start-android-emulator.sh --wait # or let smoke auto-start headless AVD when no phone is plugged in STACK=goa ./run-android-gui-smoke.sh # laptop: show the emulator window WINDOWED=1 ./start-android-emulator.sh --wait # or: EMULATOR_HEADLESS=0 EMULATOR_GPU=host ./start-android-emulator.sh --wait ``` Shared code: `lib_android_env.sh` + `start-android-emulator.sh`. **Semantics:** | `AUTO_ANDROID` | `AUTO_GUI` | Behaviour | |----------------|------------|-----------| | `1` | `0` | Android install + deep-link smoke only (no multi-round taps) | | `1` | `1` | Android + graphical drive (vanilla GUI / chain) | | `0` | `1` | Reserved (e.g. future desktop/web GUI); currently **no-op** with a clear message | | `0` | `0` | Skip mobile automation | ### Defaults by host (when flags unset) | Host (`uname -s`) | Default `AUTO_ANDROID` | Default `AUTO_GUI` | Rationale | |-------------------|------------------------|--------------------|-----------| | **Linux** | **`1`** | **`1`** if device/emulator present, else scripts exit 3 | This repo’s day-to-day path; **Android only** (no iOS here) | | **Darwin (macOS)** | `1` | `1` | Can run Android emulators **and** (separately) taler-ios GUI helpers; both flags on for Android scripts; iOS is out of tree | | **Other** | `0` | `0` | Fail closed | **Linux default = Android** (no second mobile platform in this suite). **macOS** may enable both ecosystems in the wider monorepo sense; for *these* scripts only Android is implemented — set `AUTO_ANDROID=1` (default) and keep iOS under `taler-ios`. ### Resolution helper (convention for future wrappers) ```bash # Example for a future run-all-mobile.sh os=$(uname -s) : "${AUTO_PLATFORM:=auto}" case "$AUTO_PLATFORM" in auto) case "$os" in Linux) AUTO_PLATFORM=linux ;; Darwin) AUTO_PLATFORM=macos ;; *) AUTO_PLATFORM=other ;; esac ;; esac case "$AUTO_PLATFORM" in linux) : "${AUTO_ANDROID:=1}" : "${AUTO_GUI:=1}" # no iOS ;; macos) : "${AUTO_ANDROID:=1}" : "${AUTO_GUI:=1}" # optional later: AUTO_IOS=1 for taler-ios scripts ;; *) : "${AUTO_ANDROID:=0}" : "${AUTO_GUI:=0}" ;; esac ``` Scripts **today** implement Android only; they should honour: - `AUTO_ANDROID=0` → exit 0 with “skipped (AUTO_ANDROID=0)” - `AUTO_GUI=0` → call deep-link smoke without multi-round GUI (or set `GUI_ROUNDS=0`) --- ## Vanilla layers (detail) | Layer | What | Tooling | |-------|------|---------| | **A — Install** | F-Droid or from-source APK | `adb install` | | **B — Entry (shortcut)** | `taler://withdraw/…` / `taler://pay…` | `adb am start -a VIEW` | | **C — Graphical UI** | Confirm / ToS / Pay taps; ANR Wait | `uiautomator` + `lib_ui.py` / chain | | **D — Evidence** | Screenshots, XML, logcat, URIs | `out*/` | ### Legitimate shortcuts | Shortcut | Replaces | |----------|----------| | Deep-link withdraw/pay | Camera QR / opening paywall | | Host `POST` template → pay URI | Browser shop checkout UI | | Label-list taps | Human reading button text | | Skip full ToS scroll | Tap Accept if shown | ### Scripts | Script | Role | |--------|------| | `run-android-pay-smoke.sh` | Hybrid: install + deep-link + light taps | | `run-android-gui-smoke.sh` | Multi-round GUI drive | | `run-goa-gui-chain.sh` | Multi mint/withdraw/pay (gui-workflows port) | | `run-android-build-and-smoke.sh` | Build git ref (branch/tag) + smoke (`GUI=1` optional) | | `run-android-variant-matrix.sh` | **published + stable-self + master** (compare builds) | | `start-android-emulator.sh` | Start AVD (**headless default**, SwiftShader) | | `lib_android_env.sh` | Flags, PATH, `android_ensure_device` | | `lib_ui.py` | Dump/tap/ANR helpers | ### Build variants (anti–“weird local build”) | Variant | Source | Severity | Purpose | |---------|--------|----------|---------| | **stable-self** | tag `wallet-1.6.1` rebuilt here | **WARN only** | Same release tree, our Gradle — never hard-blocks | | **published** | F-Droid APK URL | **BLOCKER** | What users install; FAIL_FAST stops here | | **master** | `origin/master` | **WARN only** | Tip of mainline — never hard-blocks | | **fix** (optional) | inference fix branch | soft fail | GOA automation fixes | Default matrix order: `stable-self,published,master`. `./run-android-variant-matrix.sh` — see README. --- ## Stacks under test | Stack | Withdraw | Pay | |-------|----------|-----| | **GOA** | explorer mint / `demo-withdraw.json` @ bank.hacktivism.ch | goa-shop templates / Paivana pay-template | | **stage** | explorer / demo-withdraw @ stage.bank… | fermes / jardin public templates | --- ## Upstream branches (`taler-android` `dev/hernani-inference/*`) | Branch | Role | |--------|------| | `gui-workflows` | macOS emulator helpers + original `goa-chain-emu.sh` | | `fix/bank-withdraw-auto-exchange` | **Minimal** GOA exchange auto-add (default build branch) | | `fix/withdraw-spinner-fallback` | Spinner + OIM; use if spinner still hangs | | `experimental-oim*` | Optional cash UI | --- ## Checklist - [x] Minimal variant documented - [x] Minimalvariante: was geht / was nicht (Beschränkungen) - [x] Two stacks (goa / stage) - [x] Flags design (`AUTO_ANDROID` / `AUTO_GUI` / platform defaults) - [x] **Headless emulator default** (`EMULATOR_HEADLESS=1`, SwiftShader) - [x] Linux default = Android suite only - [x] Inference-branch policy - [ ] Wrapper enforces flags in every entry script (incremental) - [ ] Reliable unattended Confirm on low-RAM Linux emulator