monitoring: port GOA GUI chain from taler-android inference branches
Document dev/hernani-inference/gui-workflows and related fix branches; add Linux-capable run-goa-gui-chain.sh (mint + deep-link + UI taps).
This commit is contained in:
parent
f1b6eb8b45
commit
4d49657a63
3 changed files with 413 additions and 2 deletions
|
|
@ -100,11 +100,61 @@ balance/settlement is not asserted (settlement still better covered by
|
|||
| `run-android-gui-smoke.sh` | **Android app UI** surface for the same stacks |
|
||||
| Combined | CLI = money path green; GUI = app UX green as far as taps go |
|
||||
|
||||
## Upstream: `taler-android` `dev/hernani-inference/*`
|
||||
|
||||
Branch name is **`hernani-inference`** (not “inferenced”). Remote:
|
||||
`git@git.taler.net:taler-android.git`.
|
||||
|
||||
| Branch | Relevance for automation |
|
||||
|--------|---------------------------|
|
||||
| **`dev/hernani-inference/gui-workflows`** | **Primary:** macOS emulator helpers + **GOA GUI chain** (`scripts/goa-chain-emu.sh`, `docs/gui-workflows.md`, `setup-avds.sh`, `start-emulators.sh`). Deep-link + `uiautomator` text taps + bank API mint + shop templates. |
|
||||
| **`fix/bank-withdraw-auto-exchange`** | App fix: resolve exchange when bank omits it (needed for GOA withdraw UI). |
|
||||
| **`fix/withdraw-spinner-fallback`** | App fix: stop endless withdraw spinner (+ OIM UI work). |
|
||||
| **`experimental-oim` / `experimental-oim-pos`** | Experimental cash denomination UI; optional for demos, not required for chain. |
|
||||
|
||||
### Port into this tree (Linux-capable)
|
||||
|
||||
| Upstream (gui-workflows) | Here (`android-test/`) |
|
||||
|--------------------------|-------------------------|
|
||||
| `goa-chain-emu.sh` | **`run-goa-gui-chain.sh`** (goa + stage, Linux paths, explorer secrets as monitoring ladder) |
|
||||
| `tap_text` / `click_through` | same idea in chain + `lib_ui.py` |
|
||||
| `mint_withdraw` explorer API | same (Basic → token → POST withdrawals) |
|
||||
| `mint_pay` public templates | same (goa-shop products / stage fermes templates) |
|
||||
| `android-env.sh` Homebrew | use `$HOME/Android/Sdk` + system `adb` (see GIT-AUTOMATION-NOTES) |
|
||||
| macOS-only docs | **Linux supported** for chain/smoke; AVD setup still DIY on Linux |
|
||||
|
||||
Open issues mirrored from upstream `ISSUES.md` (gui-workflows):
|
||||
|
||||
- **I-and-gui-001** — taps still best-effort; ToS/confirm may need human
|
||||
- **I-and-gui-003** — `EXP_PW_FILE` required for mint
|
||||
- **I-and-gui-004** — emulator RAM cost (this host ~3.7 GiB)
|
||||
- **I-and-gui-007** — for reliable withdraw GUI, build/install app from **fix/bank-withdraw-auto-exchange** or **fix/withdraw-spinner-fallback**, not plain master alone
|
||||
|
||||
Recommended demo stack:
|
||||
|
||||
```text
|
||||
1) Checkout taler-android: fix/withdraw-spinner-fallback (or merge fixes)
|
||||
2) ./gradlew :wallet:installFdroidDebug
|
||||
3) Boot emulator / phone
|
||||
4) android-test/run-goa-gui-chain.sh # STACK=goa|stage
|
||||
```
|
||||
|
||||
```bash
|
||||
# GOA chain (explorer mint + GUI taps)
|
||||
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
|
||||
|
||||
# Stage TESTPAYSAN (explorer via stagepaysan SSH if EXP_PW_FILE unset)
|
||||
STACK=stage ./run-goa-gui-chain.sh
|
||||
```
|
||||
|
||||
## Future upgrades (not required for vanilla)
|
||||
|
||||
- Maestro / Appium flows with stable resource-ids
|
||||
- Explicit wait for text “Withdrawal complete” / balance
|
||||
- Separate jobs: F-Droid vs nightly source vs Play flavor
|
||||
- Port Paivana unlock scripts (upstream I-and-gui-002)
|
||||
- Dual AVD wallet+POS (upstream I-and-gui-008)
|
||||
- Record video (`adb shell screenrecord`) on failure
|
||||
|
||||
## Checklist — “vanille erreicht?”
|
||||
|
|
@ -114,6 +164,9 @@ balance/settlement is not asserted (settlement still better covered by
|
|||
- [x] Source-build path (same smoke after build)
|
||||
- [x] Deep-link shortcuts documented
|
||||
- [x] Multi-round **graphical** taps (uiautomator)
|
||||
- [x] GOA-style **chain** (multi withdraw + multi pay) from inference branch
|
||||
- [x] Screenshots + UI XML + logcat artifacts
|
||||
- [x] Inference branch mapping documented
|
||||
- [ ] Fully reliable unattended Confirm→settled on low-RAM emulator (host-limited)
|
||||
- [ ] App built from withdraw-fix branch by default in build script (optional)
|
||||
- [ ] Assert final balance in app UI (optional next step)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,19 @@ STACK=stage ./run-android-gui-smoke.sh
|
|||
GUI=1 ./run-android-build-and-smoke.sh
|
||||
```
|
||||
|
||||
Documented shortcuts and limits: **[AUTOMATED-GUI-NOTES.md](./AUTOMATED-GUI-NOTES.md)**.
|
||||
## 4) Full GOA/stage chain (from `dev/hernani-inference/gui-workflows`)
|
||||
|
||||
Bank API mint → withdraw deep-link → taps → template pay → taps:
|
||||
|
||||
```bash
|
||||
# needs explorer password + device
|
||||
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
|
||||
|
||||
STACK=stage ./run-goa-gui-chain.sh
|
||||
```
|
||||
|
||||
Documented shortcuts, inference branches, limits: **[AUTOMATED-GUI-NOTES.md](./AUTOMATED-GUI-NOTES.md)**.
|
||||
|
||||
See also parent [GIT-AUTOMATION-NOTES.md](../GIT-AUTOMATION-NOTES.md).
|
||||
|
|
|
|||
345
scripts/taler-monitoring/android-test/run-goa-gui-chain.sh
Executable file
345
scripts/taler-monitoring/android-test/run-goa-gui-chain.sh
Executable file
|
|
@ -0,0 +1,345 @@
|
|||
#!/usr/bin/env bash
|
||||
# GOA (and optionally stage) GUI chain on Android — adapted from
|
||||
# taler-android branch **dev/hernani-inference/gui-workflows**
|
||||
# (`scripts/goa-chain-emu.sh`, docs/gui-workflows.md).
|
||||
#
|
||||
# Linux + macOS: uses adb + uiautomator only (no Homebrew hard dependency).
|
||||
# Upstream scripts are documented as macOS-only for SDK install paths.
|
||||
#
|
||||
# Flow:
|
||||
# 1) API mint withdraw (explorer) → VIEW taler://withdraw → GUI taps
|
||||
# 2) Merchant template POST → VIEW taler://pay → GUI taps
|
||||
#
|
||||
# Usage:
|
||||
# ./run-goa-gui-chain.sh
|
||||
# STACK=stage ./run-goa-gui-chain.sh
|
||||
# WITHDRAW_AMOUNTS='GOA:10' PAY_LIMIT=2 ./run-goa-gui-chain.sh
|
||||
# PKG=net.taler.wallet.fdroid.debug ./run-goa-gui-chain.sh --no-clear
|
||||
#
|
||||
# Env: ANDROID_SERIAL, PKG, EXP_PW_FILE, BANK, MERCHANT, INSTANCE,
|
||||
# WITHDRAW_AMOUNTS, PRODUCTS, PAY_LIMIT, SHOTDIR, PAUSE
|
||||
#
|
||||
# See AUTOMATED-GUI-NOTES.md § "hernani-inference branches"
|
||||
#
|
||||
set -euo pipefail
|
||||
ROOT=$(cd "$(dirname "$0")" && pwd)
|
||||
|
||||
CLEAR=1
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--no-clear) CLEAR=0 ;;
|
||||
-h|--help) sed -n '2,28p' "$0"; exit 0 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
die() { echo "ERROR: $*" >&2; exit 1; }
|
||||
step() { echo; echo "======== $* ========"; }
|
||||
info() { echo " $*"; }
|
||||
pause() { sleep "${1:-${PAUSE:-0.8}}"; }
|
||||
|
||||
if ! command -v adb >/dev/null 2>&1; then
|
||||
[ -x "$HOME/Android/Sdk/platform-tools/adb" ] && export PATH="$HOME/Android/Sdk/platform-tools:$PATH"
|
||||
fi
|
||||
command -v adb >/dev/null || die "adb not found"
|
||||
command -v python3 >/dev/null || die "python3 required"
|
||||
|
||||
STACK="${STACK:-goa}"
|
||||
case "$STACK" in
|
||||
goa|hacktivism)
|
||||
PKG="${PKG:-net.taler.wallet.fdroid.debug}"
|
||||
BANK="${BANK:-https://bank.hacktivism.ch}"
|
||||
MERCHANT="${MERCHANT:-https://taler.hacktivism.ch}"
|
||||
INSTANCE="${INSTANCE:-goa-shop}"
|
||||
DONATE_INSTANCE="${DONATE_INSTANCE:-goa-demo-cp4zqk}"
|
||||
DONATE_TEMPLATE="${DONATE_TEMPLATE:-goa-free}"
|
||||
DEFAULT_AMOUNTS=("GOA:10" "GOA:20")
|
||||
DEFAULT_PAYS=("product:orbit-sticker" "product:nebula-coffee" "product:voidwave-playlist" "donate:GOA:12")
|
||||
CUR_HINT=GOA
|
||||
;;
|
||||
stage|testpaysan)
|
||||
PKG="${PKG:-net.taler.wallet.fdroid.debug}"
|
||||
BANK="${BANK:-https://stage.bank.lefrancpaysan.ch}"
|
||||
MERCHANT="${MERCHANT:-https://stage.monnaie.lefrancpaysan.ch}"
|
||||
INSTANCE="${INSTANCE:-fermes-des-collines}"
|
||||
DONATE_INSTANCE="${DONATE_INSTANCE:-fermes-des-collines}"
|
||||
DONATE_TEMPLATE="${DONATE_TEMPLATE:-don-panier-libre}"
|
||||
DEFAULT_AMOUNTS=("TESTPAYSAN:10" "TESTPAYSAN:20")
|
||||
DEFAULT_PAYS=("product:panier-legumes" "product:fromage-chevre" "product:oeufs-6")
|
||||
CUR_HINT=TESTPAYSAN
|
||||
;;
|
||||
*) die "STACK must be goa|stage (got $STACK)" ;;
|
||||
esac
|
||||
|
||||
ACT="${ACT:-$PKG/net.taler.wallet.main.MainActivity}"
|
||||
EXP_USER="${EXP_USER:-explorer}"
|
||||
PAY_LIMIT="${PAY_LIMIT:-4}"
|
||||
PAUSE="${PAUSE:-0.8}"
|
||||
SHOTDIR="${SHOTDIR:-$ROOT/out-gui-chain/$(date +%Y%m%d-%H%M%S)-$STACK}"
|
||||
mkdir -p "$SHOTDIR"
|
||||
|
||||
# Explorer password (same search as taler-monitoring ladder)
|
||||
if [[ -z "${EXP_PW_FILE:-}" ]]; then
|
||||
for cand in \
|
||||
"${HOME}/src/koopa/koopa-admin-secrets/koopa/host-root/taler-bank/bank-explorer-password.txt" \
|
||||
"${HOME}/.config/taler-landing/bank-explorer-password.txt"
|
||||
do
|
||||
[[ -f "$cand" ]] && EXP_PW_FILE="$cand" && break
|
||||
done
|
||||
fi
|
||||
# stage: try stagepaysan secret via ssh if missing
|
||||
if [[ ! -f "${EXP_PW_FILE:-}" && "$STACK" = "stage" ]]; then
|
||||
for host in francpaysan-stage-user francpaysan-host; do
|
||||
tmp=$(mktemp)
|
||||
if ssh -o BatchMode=yes -o ConnectTimeout=10 "$host" \
|
||||
'tr -d "\n\r" </mnt/data/stagepaysan/bank/secrets/bank-explorer-password.txt' \
|
||||
>"$tmp" 2>/dev/null && [[ -s "$tmp" ]]; then
|
||||
EXP_PW_FILE="$tmp"
|
||||
info "explorer password via $host"
|
||||
break
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
done
|
||||
fi
|
||||
[[ -n "${EXP_PW_FILE:-}" && -f "$EXP_PW_FILE" ]] || die "set EXP_PW_FILE (explorer password)"
|
||||
|
||||
if [[ -z "${ANDROID_SERIAL:-}" ]]; then
|
||||
n=$(adb devices | awk 'NR>1 && $2=="device"{c++} END{print c+0}')
|
||||
[[ "$n" -ge 1 ]] || die "no adb device (start emulator or plug phone)"
|
||||
export ANDROID_SERIAL=$(adb devices | awk 'NR>1 && $2=="device"{print $1; exit}')
|
||||
fi
|
||||
info "ANDROID_SERIAL=$ANDROID_SERIAL STACK=$STACK PKG=$PKG SHOTDIR=$SHOTDIR"
|
||||
ADB=(adb -s "$ANDROID_SERIAL")
|
||||
|
||||
adb_sh() { "${ADB[@]}" shell "$@"; }
|
||||
|
||||
shot() {
|
||||
local n="$1"
|
||||
adb_sh screencap -p "/sdcard/demo-$n.png" 2>/dev/null || true
|
||||
"${ADB[@]}" pull "/sdcard/demo-$n.png" "$SHOTDIR/$n.png" >/dev/null 2>&1 || true
|
||||
info "[shot] $SHOTDIR/$n.png"
|
||||
}
|
||||
|
||||
tap_text() {
|
||||
local label="$1"
|
||||
adb_sh uiautomator dump /sdcard/ui.xml >/dev/null 2>&1 || return 1
|
||||
"${ADB[@]}" pull /sdcard/ui.xml "$SHOTDIR/ui.xml" >/dev/null 2>&1 || return 1
|
||||
SERIAL="$ANDROID_SERIAL" python3 - "$label" "$SHOTDIR/ui.xml" <<'PY'
|
||||
import os, re, subprocess, sys
|
||||
from pathlib import Path
|
||||
label, path = sys.argv[1], sys.argv[2]
|
||||
serial = os.environ.get("SERIAL") or os.environ.get("ANDROID_SERIAL") or ""
|
||||
xml = Path(path).read_text(errors="replace")
|
||||
# ANR dismiss
|
||||
if "isn't responding" in xml or "reagiert nicht" in xml.lower():
|
||||
for lab in ("Wait", "Warten"):
|
||||
for m in re.finditer(rf'text="{lab}"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"', xml):
|
||||
x = (int(m.group(1))+int(m.group(3)))//2
|
||||
y = (int(m.group(2))+int(m.group(4)))//2
|
||||
subprocess.run(["adb","-s",serial,"shell","input","tap",str(x),str(y)], check=False)
|
||||
print(f"anr-wait @{x},{y}")
|
||||
sys.exit(0)
|
||||
for pat in [
|
||||
rf'text="{re.escape(label)}"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"',
|
||||
rf'text="[^"]*{re.escape(label)}[^"]*"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"',
|
||||
rf'content-desc="[^"]*{re.escape(label)}[^"]*"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"',
|
||||
]:
|
||||
for m in re.finditer(pat, xml, re.I):
|
||||
chunk = xml[max(0, m.start()-120):m.end()]
|
||||
if 'enabled="false"' in chunk:
|
||||
continue
|
||||
x = (int(m.group(1))+int(m.group(3)))//2
|
||||
y = (int(m.group(2))+int(m.group(4)))//2
|
||||
print(f"tap {label!r} @{x},{y}")
|
||||
cmd = ["adb","shell","input","tap",str(x),str(y)]
|
||||
if serial:
|
||||
cmd = ["adb","-s",serial,"shell","input","tap",str(x),str(y)]
|
||||
subprocess.run(cmd, check=False)
|
||||
sys.exit(0)
|
||||
print("miss", label)
|
||||
sys.exit(1)
|
||||
PY
|
||||
}
|
||||
|
||||
mint_withdraw() {
|
||||
local amount="$1"
|
||||
EXP_USER="$EXP_USER" EXP_PW_FILE="$EXP_PW_FILE" BANK="$BANK" \
|
||||
python3 - "$amount" <<'PY'
|
||||
import base64, json, os, ssl, sys, urllib.request
|
||||
from pathlib import Path
|
||||
amount = sys.argv[1]
|
||||
bank = os.environ["BANK"].rstrip("/")
|
||||
user = os.environ["EXP_USER"]
|
||||
pw = Path(os.environ["EXP_PW_FILE"]).read_text().strip()
|
||||
auth = "Basic " + base64.b64encode(f"{user}:{pw}".encode()).decode()
|
||||
ctx = ssl.create_default_context()
|
||||
|
||||
def req(method, url, data=None, headers=None):
|
||||
h = dict(headers or {})
|
||||
body = None
|
||||
if data is not None:
|
||||
body = json.dumps(data).encode()
|
||||
h["Content-Type"] = "application/json"
|
||||
r = urllib.request.Request(url, data=body, headers=h, method=method)
|
||||
with urllib.request.urlopen(r, context=ctx, timeout=25) as resp:
|
||||
return json.load(resp)
|
||||
|
||||
tok = req(
|
||||
"POST",
|
||||
f"{bank}/accounts/{user}/token",
|
||||
{"scope": "readwrite", "duration": {"d_us": 3600_000_000}},
|
||||
{"Authorization": auth},
|
||||
)["access_token"]
|
||||
wd = req(
|
||||
"POST",
|
||||
f"{bank}/accounts/{user}/withdrawals",
|
||||
{"amount": amount},
|
||||
{"Authorization": f"Bearer {tok}"},
|
||||
)
|
||||
print(wd.get("taler_withdraw_uri") or wd.get("talerWithdrawUri") or "")
|
||||
PY
|
||||
}
|
||||
|
||||
mint_pay() {
|
||||
local mode="$1"
|
||||
MERCHANT="$MERCHANT" INSTANCE="$INSTANCE" \
|
||||
DONATE_INSTANCE="$DONATE_INSTANCE" DONATE_TEMPLATE="$DONATE_TEMPLATE" \
|
||||
python3 - "$mode" <<'PY'
|
||||
import json, os, ssl, sys, urllib.error, urllib.request
|
||||
mode = sys.argv[1]
|
||||
merchant = os.environ["MERCHANT"].rstrip("/")
|
||||
inst = os.environ["INSTANCE"]
|
||||
d_inst = os.environ["DONATE_INSTANCE"]
|
||||
d_tmpl = os.environ["DONATE_TEMPLATE"]
|
||||
ctx = ssl.create_default_context()
|
||||
|
||||
def post(url, data):
|
||||
body = json.dumps(data).encode()
|
||||
r = urllib.request.Request(url, data=body, method="POST",
|
||||
headers={"Content-Type": "application/json"})
|
||||
try:
|
||||
with urllib.request.urlopen(r, context=ctx, timeout=25) as resp:
|
||||
return resp.status, json.load(resp)
|
||||
except urllib.error.HTTPError as e:
|
||||
raw = e.read() if hasattr(e, "read") else b""
|
||||
try:
|
||||
return getattr(e, "code", None), json.loads(raw)
|
||||
except Exception as err:
|
||||
raise SystemExit(f"POST {url} failed: {e} body={raw[:200]!r}") from err
|
||||
|
||||
if mode.startswith("product:"):
|
||||
pid = mode.split(":", 1)[1]
|
||||
st, d = post(f"{merchant}/instances/{inst}/templates/{pid}", {})
|
||||
use_inst = inst
|
||||
elif mode.startswith("donate:"):
|
||||
amt = mode.split(":", 1)[1]
|
||||
st, d = post(f"{merchant}/instances/{d_inst}/templates/{d_tmpl}", {"amount": amt})
|
||||
use_inst = d_inst
|
||||
else:
|
||||
raise SystemExit(f"bad mode {mode!r}")
|
||||
|
||||
if "taler_pay_uri" in d:
|
||||
print(d["taler_pay_uri"]); raise SystemExit(0)
|
||||
oid, tok = d.get("order_id"), d.get("token")
|
||||
if not oid or not tok:
|
||||
raise SystemExit(f"no order: {d!r}"[:200])
|
||||
host = merchant.replace("https://","").replace("http://","").split("/")[0]
|
||||
print(f"taler://pay/{host}/instances/{use_inst}/{oid}/?c={tok}")
|
||||
PY
|
||||
}
|
||||
|
||||
click_through() {
|
||||
local labels=("$@")
|
||||
local i lab
|
||||
for i in 1 2 3 4 5 6 7 8; do
|
||||
for lab in "${labels[@]}"; do
|
||||
if tap_text "$lab" 2>/dev/null; then
|
||||
pause 0.9
|
||||
fi
|
||||
done
|
||||
pause 0.4
|
||||
done
|
||||
}
|
||||
|
||||
if [[ -n "${WITHDRAW_AMOUNTS:-}" ]]; then
|
||||
# shellcheck disable=SC2206
|
||||
AMOUNTS=($WITHDRAW_AMOUNTS)
|
||||
else
|
||||
AMOUNTS=("${DEFAULT_AMOUNTS[@]}")
|
||||
fi
|
||||
if [[ -n "${PRODUCTS:-}" ]]; then
|
||||
PAYS=()
|
||||
for p in $PRODUCTS; do PAYS+=("product:$p"); done
|
||||
else
|
||||
PAYS=("${DEFAULT_PAYS[@]}")
|
||||
fi
|
||||
|
||||
############################
|
||||
step "0 device + wallet ($STACK / $CUR_HINT)"
|
||||
adb_sh input keyevent KEYCODE_WAKEUP 2>/dev/null || true
|
||||
adb_sh wm dismiss-keyguard 2>/dev/null || true
|
||||
if [[ "$CLEAR" -eq 1 ]]; then
|
||||
info "pm clear $PKG"
|
||||
adb_sh pm clear "$PKG" >/dev/null 2>&1 || true
|
||||
fi
|
||||
adb_sh am force-stop "$PKG" 2>/dev/null || true
|
||||
pause 0.4
|
||||
# Prefer explicit MainActivity (from gui-workflows); fall back to VIEW launcher
|
||||
if ! adb_sh am start -n "$ACT" >/dev/null 2>&1; then
|
||||
info "MainActivity path failed — monkey launch"
|
||||
adb_sh monkey -p "$PKG" -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || true
|
||||
fi
|
||||
pause 2.0
|
||||
shot "00-start"
|
||||
|
||||
############################
|
||||
step "2 withdraw chain"
|
||||
idx=0
|
||||
for AMT in "${AMOUNTS[@]}"; do
|
||||
idx=$((idx + 1))
|
||||
step "2.$idx withdraw $AMT"
|
||||
URI="$(mint_withdraw "$AMT" | head -1)"
|
||||
[[ -n "$URI" ]] || die "mint failed for $AMT"
|
||||
info "URI=$URI"
|
||||
echo "$URI" >>"$SHOTDIR/withdraw-uris.txt"
|
||||
adb_sh am start -a android.intent.action.VIEW -d "$URI" "$PKG" >/dev/null
|
||||
pause 1.5
|
||||
shot "w${idx}-open"
|
||||
click_through "Accept" "I accept" "Accept terms" "Confirm" "Continue" "Withdraw" "Next" "OK" "Agree" "Bestätigen" "Abheben" "Akzeptieren"
|
||||
pause 3.0
|
||||
shot "w${idx}-after"
|
||||
adb_sh am start -n "$ACT" >/dev/null 2>&1 || true
|
||||
pause 0.6
|
||||
tap_text "Assets" 2>/dev/null || true
|
||||
pause 0.4
|
||||
shot "w${idx}-assets"
|
||||
done
|
||||
|
||||
############################
|
||||
step "3 pay chain (limit=$PAY_LIMIT)"
|
||||
pidx=0
|
||||
for PAY in "${PAYS[@]}"; do
|
||||
pidx=$((pidx + 1))
|
||||
[[ "$pidx" -gt "$PAY_LIMIT" ]] && break
|
||||
step "3.$pidx pay $PAY"
|
||||
if ! PAYURI="$(mint_pay "$PAY" | head -1)"; then
|
||||
info "mint_pay failed for $PAY — skip"
|
||||
continue
|
||||
fi
|
||||
[[ -n "$PAYURI" ]] || { info "empty pay uri — skip"; continue; }
|
||||
info "PAYURI=$PAYURI"
|
||||
echo "$PAYURI" >>"$SHOTDIR/pay-uris.txt"
|
||||
adb_sh am start -a android.intent.action.VIEW -d "$PAYURI" "$PKG" >/dev/null
|
||||
pause 1.5
|
||||
shot "p${pidx}-open"
|
||||
click_through "Pay" "Confirm" "Accept" "Continue" "OK" "Next" "Bezahlen" "Bestätigen" "Payer"
|
||||
pause 1.5
|
||||
shot "p${pidx}-after"
|
||||
adb_sh am start -n "$ACT" >/dev/null 2>&1 || true
|
||||
pause 0.5
|
||||
done
|
||||
|
||||
step "4 done → $SHOTDIR"
|
||||
ls -la "$SHOTDIR" | tail -40
|
||||
adb_sh dumpsys window 2>/dev/null | grep mCurrentFocus | head -1 || true
|
||||
info "PKG=$PKG amounts=${AMOUNTS[*]} pays(limit)=$PAY_LIMIT"
|
||||
echo "See AUTOMATED-GUI-NOTES.md (ported from taler-android dev/hernani-inference/gui-workflows)"
|
||||
Loading…
Add table
Add a link
Reference in a new issue