mirror of
https://github.com/zapstore/zapstore.git
synced 2026-10-05 12:38:24 +00:00
stabilize TEST-003 flows and add CLI usage guide
Improve Maestro reliability across Samsung UI states, add stage-based script ergonomics, and document copy-paste commands for device selection and stage execution. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
4fd2fc05fc
commit
d056f98d23
@@ -378,8 +378,24 @@ abstract class PackageManager extends StateNotifier<PackageManagerState> {
|
||||
) async {
|
||||
await _ensureDownloaderReady();
|
||||
|
||||
// Filter out items that already have operations
|
||||
final toQueue = items.where((item) => !hasOperation(item.appId)).toList();
|
||||
// Filter out items with in-flight operations only.
|
||||
// Terminal operations are cleared so "Update All" can re-queue apps.
|
||||
final toQueue = <({String appId, FileMetadata target, String? displayName})>[];
|
||||
for (final item in items) {
|
||||
final existing = getOperation(item.appId);
|
||||
if (existing == null) {
|
||||
toQueue.add(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existing is Completed ||
|
||||
existing is OperationFailed ||
|
||||
existing is InstallCancelled) {
|
||||
clearOperation(item.appId);
|
||||
toQueue.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (toQueue.isEmpty) return;
|
||||
|
||||
// Queue items with staggered delays to prevent UI flood
|
||||
|
||||
@@ -15,22 +15,11 @@ appId: dev.zapstore.app
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
|
||||
- assertVisible:
|
||||
text: ".*Tab 3 of 3"
|
||||
|
||||
- tapOn:
|
||||
text: ".*Tab 3 of 3"
|
||||
label: "Tap Profile tab"
|
||||
- tapOn:
|
||||
text: ".*Tab 3 of 3"
|
||||
label: "Tap Profile tab again to reset to root"
|
||||
|
||||
- waitForAnimationToEnd
|
||||
|
||||
- scrollUntilVisible:
|
||||
element:
|
||||
text: "Sign Out"
|
||||
direction: UP
|
||||
|
||||
- assertVisible:
|
||||
text: "Sign Out"
|
||||
- assertVisible:
|
||||
text: ".*npub1lhr.*"
|
||||
|
||||
@@ -28,33 +28,49 @@ appId: dev.zapstore.app
|
||||
element:
|
||||
text: ".*All Versions.*"
|
||||
direction: DOWN
|
||||
speed: 95
|
||||
timeout: 12000
|
||||
|
||||
- tapOn:
|
||||
text: ".*All Versions.*"
|
||||
label: "Expand versions list"
|
||||
|
||||
- waitForAnimationToEnd
|
||||
|
||||
# Scroll to make more versions visible
|
||||
- scroll
|
||||
|
||||
# Use "below" selector relative to the "All Versions" header:
|
||||
# index 0 = latest version's Install
|
||||
# index 1 = second (older) version's Install
|
||||
# Move list viewport so the install action targets an older entry,
|
||||
# not the top/latest one.
|
||||
- scroll
|
||||
|
||||
# Use "below" selector relative to visible version rows:
|
||||
# index 0 = first visible version install
|
||||
# index 1 = next (older) version install
|
||||
- tapOn:
|
||||
text: "Install"
|
||||
below:
|
||||
text: ".*All Versions.*"
|
||||
text: ".*Version .*"
|
||||
index: 1
|
||||
label: "Install older version"
|
||||
|
||||
# Android may open security settings to allow unknown sources.
|
||||
# Enable and return, then trigger install again.
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Install unknown apps"
|
||||
commands:
|
||||
- tapOn: "Allow from this source"
|
||||
- pressKey: back
|
||||
- scroll
|
||||
- tapOn:
|
||||
text: "Install"
|
||||
below:
|
||||
text: ".*Version .*"
|
||||
index: 1
|
||||
label: "Install older version (retry after allowing unknown source)"
|
||||
|
||||
# Wait for the first dialog to appear.
|
||||
# Regex must match the FULL accessibilityText / text of each element
|
||||
# (Java matches()), so use .* wildcards around partial phrases.
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "(?:.*Do you want to (?:update|install).*|Accept|Trust and install app)"
|
||||
timeout: 60000
|
||||
- assertVisible:
|
||||
text: "(?:.*Do you want to (?:update|install).*|.*(?:Install|Update) this app\\?.*|Accept|Trust and install app)"
|
||||
|
||||
# Amber may ask to sign a request during install
|
||||
- runFlow:
|
||||
@@ -62,8 +78,6 @@ appId: dev.zapstore.app
|
||||
visible: "Accept"
|
||||
commands:
|
||||
- tapOn: "Accept"
|
||||
- assertVisible:
|
||||
text: "(?:.*Do you want to (?:update|install).*|Trust and install app)"
|
||||
|
||||
# Samsung security dialog (may not appear if source already trusted)
|
||||
- runFlow:
|
||||
@@ -71,14 +85,12 @@ appId: dev.zapstore.app
|
||||
visible: "Trust and install app"
|
||||
commands:
|
||||
- tapOn: "Trust and install app"
|
||||
- assertVisible:
|
||||
text: ".*Do you want to (?:update|install).*"
|
||||
|
||||
# Handle Android system install dialog
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: ".*Do you want to install.*"
|
||||
text: "(?:.*Do you want to install.*|.*Install this app\\?.*)"
|
||||
commands:
|
||||
- tapOn: "Install"
|
||||
|
||||
@@ -86,16 +98,10 @@ appId: dev.zapstore.app
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: ".*Do you want to update.*"
|
||||
text: "(?:.*Do you want to update.*|.*Update this app\\?.*)"
|
||||
commands:
|
||||
- tapOn: "Update"
|
||||
|
||||
# Wait for either "App installed" / "Open" or Samsung "package analysis error".
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: "(?:Open|OK)"
|
||||
timeout: 60000
|
||||
|
||||
# Dismiss Samsung dialog if it appeared
|
||||
- runFlow:
|
||||
when:
|
||||
@@ -103,5 +109,15 @@ appId: dev.zapstore.app
|
||||
commands:
|
||||
- tapOn: "OK"
|
||||
|
||||
# Confirm install completed
|
||||
- assertVisible: "Open"
|
||||
# Some devices do not surface "Open" reliably in the same layout.
|
||||
# The script validates the installed version via adb after this flow.
|
||||
- waitForAnimationToEnd
|
||||
|
||||
# Return to app root (tab bar) so next app can be searched immediately.
|
||||
# Some devices stay on app-detail after install.
|
||||
- pressKey: back
|
||||
- runFlow:
|
||||
when:
|
||||
notVisible: ".*Tab 1 of 3"
|
||||
commands:
|
||||
- pressKey: back
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
appId: dev.zapstore.app
|
||||
---
|
||||
# Combined flow to reduce Maestro startup overhead:
|
||||
# 1) search app detail
|
||||
# 2) install older version
|
||||
- runFlow:
|
||||
file: search_app.yaml
|
||||
|
||||
- runFlow:
|
||||
file: install_older_version.yaml
|
||||
@@ -11,26 +11,23 @@ appId: dev.zapstore.app
|
||||
# After completion, the app detail page is visible.
|
||||
# =============================================================
|
||||
|
||||
# Dismiss Android notification permission dialog if shown
|
||||
# Dismiss Samsung keyboard stylus onboarding popup if shown
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Allow"
|
||||
visible: "Try out your stylus"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
- tapOn: "Cancel"
|
||||
|
||||
# Wait for the app to be ready (tab bar visible)
|
||||
- assertVisible:
|
||||
text: ".*Tab 1 of 3"
|
||||
|
||||
# Ensure we're on the Search tab root
|
||||
- tapOn:
|
||||
text: ".*Tab 1 of 3"
|
||||
label: "Tap Search tab"
|
||||
- tapOn:
|
||||
text: ".*Tab 1 of 3"
|
||||
label: "Tap Search tab again to reset"
|
||||
|
||||
- waitForAnimationToEnd
|
||||
# If tab bar is available, navigate to Search tab.
|
||||
# When Samsung keyboard overlay is active, this label may be unavailable.
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: ".*Tab 1 of 3"
|
||||
commands:
|
||||
- tapOn:
|
||||
text: ".*Tab 1 of 3"
|
||||
label: "Tap Search tab"
|
||||
|
||||
# Clear any previous search state
|
||||
- runFlow:
|
||||
@@ -39,9 +36,13 @@ appId: dev.zapstore.app
|
||||
commands:
|
||||
- tapOn: "Clear search"
|
||||
|
||||
- assertVisible: "Search apps"
|
||||
|
||||
- tapOn: "Search apps"
|
||||
- tapOn:
|
||||
text: "(?:Search apps|${APP_SEARCH_TERM})"
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Try out your stylus"
|
||||
commands:
|
||||
- tapOn: "Cancel"
|
||||
- inputText: "${APP_SEARCH_TERM}"
|
||||
- pressKey: Enter
|
||||
|
||||
@@ -50,6 +51,6 @@ appId: dev.zapstore.app
|
||||
|
||||
- tapOn:
|
||||
text: "${APP_MATCH_TEXT}"
|
||||
below:
|
||||
text: "(?:Search apps|${APP_SEARCH_TERM})"
|
||||
label: "Tap search result"
|
||||
|
||||
- waitForAnimationToEnd
|
||||
|
||||
@@ -2,27 +2,55 @@ appId: com.greenart7c3.nostrsigner
|
||||
---
|
||||
# =============================================================
|
||||
# Sub-flow: Ensure test account is active in Amber
|
||||
#
|
||||
# Handles three scenarios:
|
||||
# A) Amber main screen ("Applications") → open Accounts tab
|
||||
# B) Fresh Amber (onboarding) → import from nsec directly
|
||||
# C) Already on account selector → pick or import
|
||||
#
|
||||
# Test account npub (truncated): npub1lhr
|
||||
#
|
||||
# Environment variable required:
|
||||
# NOSTR_TEST_NSEC — the nsec to import
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Amber signer app installed on the device
|
||||
# - Device language set to English
|
||||
# =============================================================
|
||||
|
||||
# Handle Amber permission modals that may appear immediately on launch
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Permission Needed"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: "Allow Amber to send you notifications\\?"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: "Let app always run in background\\?"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
|
||||
# Wait for Amber to load (main screen or onboarding)
|
||||
- assertVisible:
|
||||
text: "(?:Applications|Use your private key|Select Account)"
|
||||
text: "(?:Applications|Use your private key|Select Account|Permission Needed|Allow Amber to send you notifications\\?|Let app always run in background\\?)"
|
||||
|
||||
# ─── Scenario A: main screen with accounts (title = "Applications") ───
|
||||
# Re-handle transient permission modals in case they reappear
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Permission Needed"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: "Allow Amber to send you notifications\\?"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: "Let app always run in background\\?"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
|
||||
# Scenario A: Amber main screen ("Applications")
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Applications"
|
||||
@@ -31,7 +59,6 @@ appId: com.greenart7c3.nostrsigner
|
||||
text: "Accounts"
|
||||
label: "Tap Accounts bottom tab"
|
||||
- assertVisible: "Select Account"
|
||||
# Import test account if not present
|
||||
- runFlow:
|
||||
when:
|
||||
notVisible: "npub1lhr"
|
||||
@@ -43,19 +70,31 @@ appId: com.greenart7c3.nostrsigner
|
||||
- tapOn:
|
||||
text: "Nsec / private key"
|
||||
label: "Tap nsec input field"
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Try out your stylus"
|
||||
commands:
|
||||
- tapOn: "Cancel"
|
||||
- inputText: "${NOSTR_TEST_NSEC}"
|
||||
- tapOn: "Next"
|
||||
- assertVisible: "Finish"
|
||||
- tapOn: "Finish"
|
||||
- assertVisible:
|
||||
text: "(?:Allow Amber|Applications)"
|
||||
text: "(?:Applications|Don.?t allow|Allow Amber to send you notifications\\?|Let app always run in background\\?)"
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Allow"
|
||||
visible:
|
||||
text: "Don.?t allow"
|
||||
commands:
|
||||
- tapOn:
|
||||
text: "Don.?t allow"
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: "Let app always run in background\\?"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
- assertVisible: "Applications"
|
||||
# If account sheet is still open (account existed), select it
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Select Account"
|
||||
@@ -63,9 +102,14 @@ appId: com.greenart7c3.nostrsigner
|
||||
- tapOn:
|
||||
text: "npub1lhr"
|
||||
label: "Select test account"
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Permission Needed"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
- pressKey: back
|
||||
|
||||
# ─── Scenario B: fresh Amber onboarding ───
|
||||
# Scenario B: Fresh onboarding
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Use your private key"
|
||||
@@ -75,15 +119,28 @@ appId: com.greenart7c3.nostrsigner
|
||||
- tapOn:
|
||||
text: "Nsec / private key"
|
||||
label: "Tap nsec input field"
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Try out your stylus"
|
||||
commands:
|
||||
- tapOn: "Cancel"
|
||||
- inputText: "${NOSTR_TEST_NSEC}"
|
||||
- tapOn: "Next"
|
||||
- assertVisible: "Finish"
|
||||
- tapOn: "Finish"
|
||||
- assertVisible:
|
||||
text: "(?:Allow Amber|Applications)"
|
||||
text: "(?:Applications|Don.?t allow|Allow Amber to send you notifications\\?|Let app always run in background\\?)"
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Allow"
|
||||
visible:
|
||||
text: "Don.?t allow"
|
||||
commands:
|
||||
- tapOn:
|
||||
text: "Don.?t allow"
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: "Let app always run in background\\?"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
- assertVisible: "Applications"
|
||||
|
||||
@@ -6,12 +6,6 @@ appId: dev.zapstore.app
|
||||
# Navigates to Profile tab and ensures the test account is active.
|
||||
# If signed in with a different account, signs out first.
|
||||
# If not signed in, signs in via Amber.
|
||||
#
|
||||
# Test account npub: npub1lhrt8n340j83f8umlm7lc23lrfkj7fd22ynkrh4zn8mc4tltqrns4fkp3n
|
||||
# (see test/fixtures/test-account.env for full keypair)
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Amber signer app installed with test account imported
|
||||
# =============================================================
|
||||
|
||||
# Dismiss Android notification permission dialog if shown
|
||||
@@ -21,24 +15,17 @@ appId: dev.zapstore.app
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
|
||||
# Wait for the app to fully load (tab bar must be visible)
|
||||
- assertVisible:
|
||||
text: ".*Tab 3 of 3"
|
||||
# Handle Android background execution prompt from Amber
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: "Let app always run in background\\?"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
|
||||
- tapOn:
|
||||
text: ".*Tab 3 of 3"
|
||||
label: "Tap Profile tab"
|
||||
- tapOn:
|
||||
text: ".*Tab 3 of 3"
|
||||
label: "Tap Profile tab again to reset to root"
|
||||
|
||||
- waitForAnimationToEnd
|
||||
|
||||
# Scroll to find sign-in/sign-out (profile page may not start at top)
|
||||
- scrollUntilVisible:
|
||||
element:
|
||||
text: "(?:Sign in with Amber|Sign Out)"
|
||||
direction: UP
|
||||
|
||||
# If signed in with wrong account, sign out first
|
||||
- runFlow:
|
||||
@@ -58,21 +45,26 @@ appId: dev.zapstore.app
|
||||
visible: "Sign in with Amber"
|
||||
commands:
|
||||
- tapOn: "Sign in with Amber"
|
||||
- assertVisible: "Connect"
|
||||
- assertVisible:
|
||||
text: "(?:Connect|Permission Needed)"
|
||||
- runFlow:
|
||||
when:
|
||||
visible: "Permission Needed"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: "Let app always run in background\\?"
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
- tapOn: "Connect"
|
||||
- scrollUntilVisible:
|
||||
element: "Sign Out"
|
||||
direction: UP
|
||||
|
||||
# After Amber sign-in, app may return to a different tab — go back to Profile
|
||||
- tapOn:
|
||||
text: ".*Tab 3 of 3"
|
||||
label: "Navigate back to Profile tab"
|
||||
|
||||
- waitForAnimationToEnd
|
||||
|
||||
# Verify we're signed in
|
||||
- scrollUntilVisible:
|
||||
element:
|
||||
text: "(?:Sign Out|npub1lhrt)"
|
||||
direction: UP
|
||||
- assertVisible:
|
||||
text: "(?:Sign Out|npub1lhrt)"
|
||||
|
||||
@@ -17,18 +17,9 @@ appId: dev.zapstore.app
|
||||
commands:
|
||||
- tapOn: "Allow"
|
||||
|
||||
# Wait for the app to be ready (tab bar visible)
|
||||
- assertVisible:
|
||||
text: ".*Tab 2 of 3"
|
||||
|
||||
- tapOn:
|
||||
text: ".*Tab 2 of 3"
|
||||
label: "Tap Updates tab"
|
||||
- tapOn:
|
||||
text: ".*Tab 2 of 3"
|
||||
label: "Tap Updates tab again to reset to root"
|
||||
|
||||
- waitForAnimationToEnd
|
||||
|
||||
# "All done (N installed)" banner and "Update All" are mutually exclusive.
|
||||
# If the banner is showing (from the install phase), dismiss it first.
|
||||
@@ -38,19 +29,34 @@ appId: dev.zapstore.app
|
||||
commands:
|
||||
- tapOn:
|
||||
text: ".*All done.*"
|
||||
- waitForAnimationToEnd
|
||||
|
||||
# Wait for updates to load (may take time after restart)
|
||||
# Wait for updates to load
|
||||
- assertVisible:
|
||||
text: ".*Update All.*"
|
||||
|
||||
# Badge/section count must match the number of apps prepared in the script
|
||||
- assertVisible:
|
||||
text: ".*Updates\\s*${APP_COUNT}.*"
|
||||
|
||||
- tapOn:
|
||||
text: ".*Update All.*"
|
||||
label: "Tap Update All"
|
||||
retryTapIfNoChange: true
|
||||
|
||||
# On some Samsung builds the first tap can be swallowed by transient UI state.
|
||||
# Retry once if the Update All button is still present.
|
||||
- runFlow:
|
||||
when:
|
||||
visible:
|
||||
text: ".*Update All.*"
|
||||
commands:
|
||||
- waitForAnimationToEnd
|
||||
- tapOn:
|
||||
text: ".*Update All.*"
|
||||
label: "Retry Tap Update All"
|
||||
retryTapIfNoChange: true
|
||||
|
||||
# Wait for the "All done (N updated)" banner.
|
||||
# Regex must consume the full accessibilityText including the trailing ")".
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
text: ".*All done.*updated.*"
|
||||
timeout: 180000
|
||||
# Require the completion banner to avoid false positives from generic "Up to date" sections.
|
||||
- assertVisible:
|
||||
text: ".*All done.*updated.*"
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# TEST-003 Script Usage
|
||||
|
||||
Script: `test/scripts/run-test-003.sh`
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
cd /Users/hvmelo/Developer/codecode/Zapstore/zapstore
|
||||
adb devices
|
||||
./test/scripts/run-test-003.sh --device <DEVICE_ID> --apps 2
|
||||
```
|
||||
|
||||
## Device Selection
|
||||
|
||||
- List devices:
|
||||
|
||||
```bash
|
||||
adb devices
|
||||
```
|
||||
|
||||
- Pass the device with `--device`:
|
||||
|
||||
```bash
|
||||
./test/scripts/run-test-003.sh --device RQCT1029N9J --apps 2
|
||||
```
|
||||
|
||||
## Stage-Based Execution
|
||||
|
||||
Stages:
|
||||
|
||||
- `0` clean_state
|
||||
- `1` auth
|
||||
- `2` install_old
|
||||
- `3` update_all
|
||||
- `4` post_verify
|
||||
|
||||
Run examples:
|
||||
|
||||
- Install old versions + update + verify:
|
||||
|
||||
```bash
|
||||
./test/scripts/run-test-003.sh --device RQCT1029N9J --apps 2 --from 2 --to 4
|
||||
```
|
||||
|
||||
- Only update-all + post verification (when apps are already prepared):
|
||||
|
||||
```bash
|
||||
./test/scripts/run-test-003.sh --device RQCT1029N9J --apps 2 --from 3 --to 4
|
||||
```
|
||||
|
||||
- Only old-version install:
|
||||
|
||||
```bash
|
||||
./test/scripts/run-test-003.sh --device RQCT1029N9J --apps 2 --from 2 --to 2
|
||||
```
|
||||
|
||||
## Help
|
||||
|
||||
```bash
|
||||
./test/scripts/run-test-003.sh --help
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- The script enforces a minimum of 2 apps (`--apps 2`) for `Update All` validation.
|
||||
- Reports are written to `test/runs/`.
|
||||
+289
-108
@@ -2,30 +2,6 @@
|
||||
#
|
||||
# TEST-003 — Bulk Update Flow (standalone, no AI needed)
|
||||
#
|
||||
# Orchestrates adb + Maestro CLI to:
|
||||
# 1. Uninstall 4 target apps via adb
|
||||
# 2. Install older versions via Zapstore UI (Maestro flows)
|
||||
# 3. Verify all updates appear in one list
|
||||
# 4. Execute "Update All"
|
||||
# 5. Verify all apps updated to latest via adb
|
||||
#
|
||||
# Prerequisites:
|
||||
# - adb in PATH, device connected via USB
|
||||
# - maestro CLI in PATH (https://maestro.mobile.dev)
|
||||
# - Zapstore (dev.zapstore.app) installed on device
|
||||
# - Amber signer app installed (test account is imported automatically)
|
||||
# - Device language set to English
|
||||
#
|
||||
# The script auto-imports the test keypair into Amber if not present.
|
||||
# The test account's pubkey is in allowedHexKeys (lib/utils/debug_utils.dart),
|
||||
# which is required for the "Debug: All Versions" section to appear.
|
||||
# See test/fixtures/test-account.env for the full keypair.
|
||||
#
|
||||
# Usage:
|
||||
# ./run-test-003.sh # auto-detect device, 4 apps
|
||||
# ./run-test-003.sh <DEVICE_ID> # target specific device, 4 apps
|
||||
# ./run-test-003.sh <DEVICE_ID> <N> # target device, N apps (1–4)
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
# ── Configuration ────────────────────────────────────────────────────
|
||||
@@ -36,9 +12,15 @@ FLOWS_DIR="$PROJECT_ROOT/maestro/flows"
|
||||
REPORT_DIR="$PROJECT_ROOT/test/runs"
|
||||
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
|
||||
REPORT_FILE="$REPORT_DIR/TEST-003-auto-$TIMESTAMP.md"
|
||||
|
||||
ZAPSTORE_PKG="dev.zapstore.app"
|
||||
AMBER_PKG="com.greenart7c3.nostrsigner"
|
||||
AMBER_APK_URL="https://github.com/greenart7c3/Amber/releases/download/v4.1.2/amber-free-universal-release-v4.1.2.apk"
|
||||
AMBER_APK_CACHE="/tmp/amber-latest.apk"
|
||||
FIXTURES_DIR="$PROJECT_ROOT/test/fixtures"
|
||||
MAX_RETRIES=3
|
||||
|
||||
# Always run from a fully clean state
|
||||
FORCE_FRESH_START=1
|
||||
|
||||
# Load test account nsec from fixtures
|
||||
NOSTR_TEST_NSEC=""
|
||||
@@ -50,8 +32,89 @@ if [ -z "$NOSTR_TEST_NSEC" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEVICE_ID="${1:-}"
|
||||
REQUESTED_COUNT="${2:-4}"
|
||||
print_usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
run-test-003.sh [DEVICE_ID] [APP_COUNT] [--from N] [--to N]
|
||||
[--device DEVICE_ID] [--apps APP_COUNT]
|
||||
|
||||
Stages:
|
||||
0 = clean_state (reset apps/data, uninstall targets, amber cache)
|
||||
1 = auth (setup Amber + sign in Zapstore)
|
||||
2 = install_old (install older versions)
|
||||
3 = update_all (verify updates + tap Update All)
|
||||
4 = post_verify (verify versions changed)
|
||||
|
||||
Examples:
|
||||
./run-test-003.sh RQCT1029N9J 2
|
||||
./run-test-003.sh --device RQCT1029N9J --apps 2 --from 2 --to 4
|
||||
./run-test-003.sh RQCT1029N9J 2 --from 3 --to 4
|
||||
EOF
|
||||
}
|
||||
|
||||
DEVICE_ID=""
|
||||
REQUESTED_COUNT="2"
|
||||
FROM_STAGE=0
|
||||
TO_STAGE=4
|
||||
|
||||
POSITIONAL=()
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--from)
|
||||
FROM_STAGE="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--to)
|
||||
TO_STAGE="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--device)
|
||||
DEVICE_ID="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--apps|--count)
|
||||
REQUESTED_COUNT="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
echo "ERROR: unknown option: $1"
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
POSITIONAL+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$DEVICE_ID" ] && [ ${#POSITIONAL[@]} -ge 1 ]; then
|
||||
DEVICE_ID="${POSITIONAL[0]}"
|
||||
fi
|
||||
if [ ${#POSITIONAL[@]} -ge 2 ]; then
|
||||
REQUESTED_COUNT="${POSITIONAL[1]}"
|
||||
fi
|
||||
|
||||
if ! [[ "$FROM_STAGE" =~ ^[0-4]$ ]]; then
|
||||
echo "ERROR: --from must be between 0 and 4"
|
||||
exit 1
|
||||
fi
|
||||
if ! [[ "$TO_STAGE" =~ ^[0-4]$ ]]; then
|
||||
echo "ERROR: --to must be between 0 and 4"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$FROM_STAGE" -gt "$TO_STAGE" ]; then
|
||||
echo "ERROR: --from cannot be greater than --to"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# App catalog (parallel arrays — bash 3.2 compatible)
|
||||
ALL_APP_NAMES=( "Flotilla" "Amethyst" "OpenBible" "DuckDuckGo" )
|
||||
@@ -61,14 +124,14 @@ ALL_APP_MATCH=( ".*Flotilla.*hodlbod.*" ".*all-in-one Nostr.*" ".*prov
|
||||
|
||||
MAX_APPS=${#ALL_APP_NAMES[@]}
|
||||
|
||||
# Clamp to 1..MAX_APPS
|
||||
if [ "$REQUESTED_COUNT" -lt 1 ] 2>/dev/null; then REQUESTED_COUNT=1; fi
|
||||
# Clamp to 2..MAX_APPS (Update All requires at least 2 apps)
|
||||
if [ "$REQUESTED_COUNT" -lt 2 ] 2>/dev/null; then REQUESTED_COUNT=2; fi
|
||||
if [ "$REQUESTED_COUNT" -gt "$MAX_APPS" ] 2>/dev/null || ! [ "$REQUESTED_COUNT" -eq "$REQUESTED_COUNT" ] 2>/dev/null; then
|
||||
REQUESTED_COUNT=$MAX_APPS
|
||||
fi
|
||||
|
||||
# Slice arrays to requested count
|
||||
APP_NAMES=(); APP_PKGS=(); APP_SEARCH=(); APP_MATCH=()
|
||||
APP_NAMES=(); APP_PKGS=(); APP_SEARCH=(); APP_MATCH=()
|
||||
VER_BEFORE=(); VER_AFTER=()
|
||||
i=0
|
||||
while [ $i -lt "$REQUESTED_COUNT" ]; do
|
||||
@@ -80,7 +143,6 @@ while [ $i -lt "$REQUESTED_COUNT" ]; do
|
||||
VER_AFTER+=( "" )
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
APP_COUNT=${#APP_NAMES[@]}
|
||||
|
||||
# ── State ────────────────────────────────────────────────────────────
|
||||
@@ -90,6 +152,7 @@ FAILURE_COUNT=0
|
||||
LOG_TEXT=""
|
||||
INSTALL_RESULTS=""
|
||||
START_TIME="$(date +%s)"
|
||||
DID_POST_VERIFY=0
|
||||
|
||||
# ── Helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -115,16 +178,89 @@ get_version() {
|
||||
|
||||
run_maestro() {
|
||||
local flow="$1"; shift
|
||||
local flow_name
|
||||
flow_name="$(basename "$flow")"
|
||||
local started
|
||||
started="$(date +%s)"
|
||||
|
||||
local env_args=()
|
||||
while [ $# -gt 0 ]; do
|
||||
env_args+=(-e "$1")
|
||||
shift
|
||||
done
|
||||
|
||||
log " -> Maestro start: $flow_name"
|
||||
|
||||
local rc=0
|
||||
if [ ${#env_args[@]} -eq 0 ]; then
|
||||
maestro test --udid "$DEVICE_ID" "$flow"
|
||||
maestro test --udid "$DEVICE_ID" "$flow" || rc=$?
|
||||
else
|
||||
maestro test --udid "$DEVICE_ID" "${env_args[@]}" "$flow"
|
||||
maestro test --udid "$DEVICE_ID" "${env_args[@]}" "$flow" || rc=$?
|
||||
fi
|
||||
|
||||
local ended elapsed
|
||||
ended="$(date +%s)"
|
||||
elapsed=$((ended - started))
|
||||
log " <- Maestro end: $flow_name (${elapsed}s, rc=$rc)"
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
disable_notifications_permission() {
|
||||
local package="$1"
|
||||
local label="$2"
|
||||
|
||||
adb -s "$DEVICE_ID" shell pm revoke "$package" android.permission.POST_NOTIFICATIONS >/dev/null 2>&1 || true
|
||||
adb -s "$DEVICE_ID" shell pm set-permission-flags "$package" android.permission.POST_NOTIFICATIONS user-set user-fixed >/dev/null 2>&1 || true
|
||||
adb -s "$DEVICE_ID" shell cmd appops set "$package" POST_NOTIFICATION ignore >/dev/null 2>&1 || true
|
||||
|
||||
log " Notifications disabled for $label"
|
||||
}
|
||||
|
||||
wait_for_foreground() {
|
||||
local package="$1"
|
||||
local timeout_secs="${2:-8}"
|
||||
local i=0
|
||||
while [ "$i" -lt "$timeout_secs" ]; do
|
||||
if adb -s "$DEVICE_ID" shell dumpsys window 2>/dev/null | grep -q "$package"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
should_run_stage() {
|
||||
local stage="$1"
|
||||
[ "$stage" -ge "$FROM_STAGE" ] && [ "$stage" -le "$TO_STAGE" ]
|
||||
}
|
||||
|
||||
prepare_stage_window_prereqs() {
|
||||
# When starting from a later stage, still do the minimal prep needed
|
||||
# for deterministic installs.
|
||||
if ! should_run_stage 0 && should_run_stage 2; then
|
||||
log "── Pre-setup for stage window: reset target install apps ──"
|
||||
uninstall_apps
|
||||
fi
|
||||
}
|
||||
|
||||
capture_versions_as_before() {
|
||||
log "── Snapshot: capture installed versions as baseline ──"
|
||||
local i=0
|
||||
while [ $i -lt $APP_COUNT ]; do
|
||||
local package="${APP_PKGS[$i]}"
|
||||
local name="${APP_NAMES[$i]}"
|
||||
local ver
|
||||
ver=$(get_version "$package")
|
||||
if [ -n "$ver" ]; then
|
||||
VER_BEFORE[$i]="$ver"
|
||||
log " Baseline $name: $ver"
|
||||
else
|
||||
VER_BEFORE[$i]="FAILED"
|
||||
log " Baseline $name: not installed"
|
||||
fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
}
|
||||
|
||||
# ── Phases ───────────────────────────────────────────────────────────
|
||||
@@ -146,7 +282,6 @@ setup_screen() {
|
||||
log "Screen stay-on enabled"
|
||||
}
|
||||
|
||||
|
||||
uninstall_apps() {
|
||||
log "── Setup: Uninstalling target apps ──"
|
||||
local i=0
|
||||
@@ -163,25 +298,59 @@ uninstall_apps() {
|
||||
done
|
||||
}
|
||||
|
||||
prepare_clean_state() {
|
||||
log "── Setup: Reset device state (clean start) ──"
|
||||
|
||||
adb -s "$DEVICE_ID" shell am force-stop "$ZAPSTORE_PKG" 2>/dev/null || true
|
||||
adb -s "$DEVICE_ID" shell am force-stop "$AMBER_PKG" 2>/dev/null || true
|
||||
adb -s "$DEVICE_ID" shell am force-stop com.android.settings 2>/dev/null || true
|
||||
|
||||
adb -s "$DEVICE_ID" shell pm clear "$ZAPSTORE_PKG" >/dev/null 2>&1 || true
|
||||
|
||||
if adb -s "$DEVICE_ID" shell pm list packages 2>/dev/null | grep -q "$AMBER_PKG"; then
|
||||
adb -s "$DEVICE_ID" shell pm uninstall "$AMBER_PKG" >/dev/null 2>&1 || true
|
||||
log " Amber uninstalled for fresh start"
|
||||
else
|
||||
log " Amber not installed, nothing to remove"
|
||||
fi
|
||||
rm -f "$AMBER_APK_CACHE" >/dev/null 2>&1 || true
|
||||
log " Amber APK cache cleared"
|
||||
|
||||
uninstall_apps
|
||||
}
|
||||
|
||||
restart_zapstore() {
|
||||
adb -s "$DEVICE_ID" shell am force-stop "$ZAPSTORE_PKG" 2>/dev/null || true
|
||||
sleep 2
|
||||
adb -s "$DEVICE_ID" shell am start --activity-clear-task \
|
||||
-n "$ZAPSTORE_PKG/.MainActivity" >/dev/null 2>&1
|
||||
sleep 10
|
||||
sleep 0.5
|
||||
adb -s "$DEVICE_ID" shell am start --activity-clear-task -n "$ZAPSTORE_PKG/.MainActivity" >/dev/null 2>&1
|
||||
wait_for_foreground "$ZAPSTORE_PKG" 8 || true
|
||||
log "Zapstore restarted"
|
||||
}
|
||||
|
||||
ensure_amber() {
|
||||
if adb -s "$DEVICE_ID" shell pm list packages 2>/dev/null | grep -q "$AMBER_PKG"; then
|
||||
log " Amber already installed"
|
||||
disable_notifications_permission "$AMBER_PKG" "Amber"
|
||||
return 0
|
||||
fi
|
||||
log " Amber not installed — downloading and installing..."
|
||||
if [ ! -f "$AMBER_APK_CACHE" ]; then
|
||||
curl -L -o "$AMBER_APK_CACHE" "$AMBER_APK_URL" || { fail "Failed to download Amber APK"; return 1; }
|
||||
fi
|
||||
adb -s "$DEVICE_ID" install "$AMBER_APK_CACHE" || { fail "Failed to install Amber APK"; return 1; }
|
||||
log " Amber installed successfully"
|
||||
disable_notifications_permission "$AMBER_PKG" "Amber"
|
||||
}
|
||||
|
||||
setup_amber() {
|
||||
log "── Phase 0: Ensure test account in Amber ──"
|
||||
# Clean up: dismiss notifications, stop stale apps, go to home screen
|
||||
adb -s "$DEVICE_ID" shell input keyevent KEYCODE_HOME 2>/dev/null || true
|
||||
adb -s "$DEVICE_ID" shell am force-stop com.android.settings 2>/dev/null || true
|
||||
adb -s "$DEVICE_ID" shell am force-stop com.greenart7c3.nostrsigner 2>/dev/null || true
|
||||
adb -s "$DEVICE_ID" shell am force-stop "$AMBER_PKG" 2>/dev/null || true
|
||||
adb -s "$DEVICE_ID" shell am force-stop "$ZAPSTORE_PKG" 2>/dev/null || true
|
||||
sleep 2
|
||||
adb -s "$DEVICE_ID" shell am start -n com.greenart7c3.nostrsigner/.MainActivity >/dev/null 2>&1
|
||||
sleep 5
|
||||
sleep 0.2
|
||||
adb -s "$DEVICE_ID" shell am start -n "$AMBER_PKG/.MainActivity" >/dev/null 2>&1
|
||||
wait_for_foreground "$AMBER_PKG" 6 || true
|
||||
if run_maestro "$FLOWS_DIR/setup_amber_test_account.yaml" "NOSTR_TEST_NSEC=$NOSTR_TEST_NSEC"; then
|
||||
log " Amber test account ready"
|
||||
else
|
||||
@@ -193,21 +362,12 @@ setup_amber() {
|
||||
|
||||
sign_in() {
|
||||
log "── Phase 1: Sign in ──"
|
||||
local attempt=0
|
||||
while [ $attempt -lt $MAX_RETRIES ]; do
|
||||
attempt=$((attempt + 1))
|
||||
log " Sign in attempt $attempt/$MAX_RETRIES"
|
||||
# Dismiss any pending Amber dialogs by force-stopping it
|
||||
adb -s "$DEVICE_ID" shell am force-stop com.greenart7c3.nostrsigner 2>/dev/null || true
|
||||
sleep 1
|
||||
if run_maestro "$FLOWS_DIR/sign_in_amber.yaml"; then
|
||||
log " Sign in verified"
|
||||
return 0
|
||||
fi
|
||||
log " Sign in attempt $attempt failed, restarting Zapstore..."
|
||||
restart_zapstore
|
||||
done
|
||||
fail "Sign in failed after $MAX_RETRIES attempts"
|
||||
adb -s "$DEVICE_ID" shell am force-stop "$AMBER_PKG" 2>/dev/null || true
|
||||
if run_maestro "$FLOWS_DIR/sign_in_amber.yaml"; then
|
||||
log " Sign in verified"
|
||||
return 0
|
||||
fi
|
||||
fail "Sign in failed"
|
||||
generate_report
|
||||
exit 1
|
||||
}
|
||||
@@ -218,37 +378,28 @@ install_older_version() {
|
||||
local package="${APP_PKGS[$idx]}"
|
||||
local search="${APP_SEARCH[$idx]}"
|
||||
local match="${APP_MATCH[$idx]}"
|
||||
local attempt=0
|
||||
log " Installing older $name"
|
||||
|
||||
while [ $attempt -lt $MAX_RETRIES ]; do
|
||||
attempt=$((attempt + 1))
|
||||
log " Installing older $name (attempt $attempt/$MAX_RETRIES)"
|
||||
# Bring Zapstore to foreground in case previous install left system/app-detail state.
|
||||
adb -s "$DEVICE_ID" shell am start -n "$ZAPSTORE_PKG/.MainActivity" >/dev/null 2>&1 || true
|
||||
wait_for_foreground "$ZAPSTORE_PKG" 6 || true
|
||||
|
||||
run_maestro "$FLOWS_DIR/search_app.yaml" \
|
||||
"APP_SEARCH_TERM=$search" "APP_MATCH_TEXT=$match" && \
|
||||
run_maestro "$FLOWS_DIR/install_older_version.yaml" || true
|
||||
run_maestro "$FLOWS_DIR/search_and_install_older.yaml" "APP_SEARCH_TERM=$search" "APP_MATCH_TEXT=$match" || true
|
||||
|
||||
# Check if the app got installed (flow might fail but install can still succeed)
|
||||
local ver
|
||||
ver=$(get_version "$package")
|
||||
if [ -n "$ver" ]; then
|
||||
VER_BEFORE[$idx]="$ver"
|
||||
log " ✓ $name installed: $ver"
|
||||
INSTALL_RESULTS="${INSTALL_RESULTS}| $name | $ver | ✓ (attempt $attempt) |
|
||||
local ver
|
||||
ver=$(get_version "$package")
|
||||
if [ -n "$ver" ]; then
|
||||
VER_BEFORE[$idx]="$ver"
|
||||
log " ✓ $name installed: $ver"
|
||||
INSTALL_RESULTS="${INSTALL_RESULTS}| $name | $ver | ✓ |
|
||||
"
|
||||
# Restart to clean state for next app (flow may have left dialogs open)
|
||||
restart_zapstore
|
||||
return 0
|
||||
fi
|
||||
|
||||
log " Attempt $attempt failed (not installed), resetting Zapstore..."
|
||||
restart_zapstore
|
||||
done
|
||||
return 0
|
||||
fi
|
||||
|
||||
VER_BEFORE[$idx]="FAILED"
|
||||
INSTALL_RESULTS="${INSTALL_RESULTS}| $name | FAILED | ✗ |
|
||||
"
|
||||
fail "Could not install older $name after $MAX_RETRIES attempts"
|
||||
fail "Could not install older $name"
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -265,7 +416,6 @@ install_all_older() {
|
||||
done
|
||||
|
||||
log " Installed $installed/$APP_COUNT apps"
|
||||
|
||||
if [ $installed -eq 0 ]; then
|
||||
fail "No apps installed — cannot proceed"
|
||||
generate_report
|
||||
@@ -275,8 +425,7 @@ install_all_older() {
|
||||
|
||||
verify_and_update() {
|
||||
log "── Phase 3+4: Verify updates & Update All ──"
|
||||
restart_zapstore
|
||||
if run_maestro "$FLOWS_DIR/verify_and_update_all.yaml"; then
|
||||
if run_maestro "$FLOWS_DIR/verify_and_update_all.yaml" "APP_COUNT=$APP_COUNT"; then
|
||||
log " Update All completed successfully"
|
||||
else
|
||||
fail "verify_and_update_all flow failed"
|
||||
@@ -285,9 +434,9 @@ verify_and_update() {
|
||||
|
||||
verify_post_update() {
|
||||
log "── Phase 5: Post-update verification ──"
|
||||
sleep 5
|
||||
sleep 0.5
|
||||
DID_POST_VERIFY=1
|
||||
local i=0
|
||||
|
||||
while [ $i -lt $APP_COUNT ]; do
|
||||
local name="${APP_NAMES[$i]}"
|
||||
local package="${APP_PKGS[$i]}"
|
||||
@@ -303,7 +452,6 @@ verify_post_update() {
|
||||
local ver
|
||||
ver=$(get_version "$package")
|
||||
VER_AFTER[$i]="$ver"
|
||||
|
||||
if [ -n "$ver" ] && [ "$ver" != "$before" ]; then
|
||||
log " ✓ $name: $before → $ver"
|
||||
else
|
||||
@@ -327,7 +475,6 @@ generate_report() {
|
||||
local result="PASS"
|
||||
[ $FAILURE_COUNT -gt 0 ] && result="FAIL"
|
||||
|
||||
# Build post-update table
|
||||
local post_update_table=""
|
||||
local i=0
|
||||
while [ $i -lt $APP_COUNT ]; do
|
||||
@@ -335,7 +482,9 @@ generate_report() {
|
||||
local before="${VER_BEFORE[$i]:-N/A}"
|
||||
local after="${VER_AFTER[$i]:-N/A}"
|
||||
local status="✓"
|
||||
if [ "$before" = "$after" ] || [ "$after" = "SKIPPED" ] || [ -z "$after" ]; then
|
||||
if [ "$after" = "SKIPPED" ] || [ -z "$after" ]; then
|
||||
status="-"
|
||||
elif [ "$before" = "$after" ]; then
|
||||
status="✗"
|
||||
fi
|
||||
post_update_table="${post_update_table}| $name | $before | $after | $status |
|
||||
@@ -343,13 +492,11 @@ generate_report() {
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
# Failures
|
||||
local failures_section="None"
|
||||
if [ $FAILURE_COUNT -gt 0 ]; then
|
||||
failures_section="$FAILURES"
|
||||
fi
|
||||
|
||||
# Criteria checks
|
||||
local c_install="x"; echo "$INSTALL_RESULTS" | grep -q "FAILED" && c_install=" "
|
||||
local c_update="x"; [ $FAILURE_COUNT -gt 0 ] && c_update=" "
|
||||
|
||||
@@ -396,23 +543,29 @@ EOF
|
||||
|
||||
# ── Main ─────────────────────────────────────────────────────────────
|
||||
|
||||
# Check if already signed in with test account; if not, setup Amber and sign in.
|
||||
# Skips Amber setup entirely when already logged in with correct user.
|
||||
# setup_amber only imports the account into Amber if it's not already there.
|
||||
# Sets ALREADY_SIGNED_IN=1 when skipped, so main won't restart Zapstore.
|
||||
ensure_signed_in() {
|
||||
if [ "${FORCE_FRESH_START:-0}" -eq 1 ]; then
|
||||
log "── Fresh start enabled: skipping signed-in check ──"
|
||||
ensure_amber
|
||||
disable_notifications_permission "$ZAPSTORE_PKG" "Zapstore"
|
||||
adb -s "$DEVICE_ID" shell am force-stop "$AMBER_PKG" 2>/dev/null || true
|
||||
setup_amber
|
||||
restart_zapstore
|
||||
sign_in
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "── Check: Already signed in with test account? ──"
|
||||
restart_zapstore
|
||||
if run_maestro "$FLOWS_DIR/check_signed_in_test_account.yaml" 2>/dev/null; then
|
||||
log " Already signed in with test account, skipping Amber setup"
|
||||
ALREADY_SIGNED_IN=1
|
||||
return 0
|
||||
fi
|
||||
ALREADY_SIGNED_IN=0
|
||||
log " Not signed in (or wrong account), setting up Amber and signing in..."
|
||||
ensure_amber
|
||||
adb -s "$DEVICE_ID" shell pm clear "$ZAPSTORE_PKG" >/dev/null 2>&1 || true
|
||||
adb -s "$DEVICE_ID" shell pm grant "$ZAPSTORE_PKG" android.permission.POST_NOTIFICATIONS 2>/dev/null || true
|
||||
adb -s "$DEVICE_ID" shell am force-stop com.greenart7c3.nostrsigner 2>/dev/null || true
|
||||
disable_notifications_permission "$ZAPSTORE_PKG" "Zapstore"
|
||||
adb -s "$DEVICE_ID" shell am force-stop "$AMBER_PKG" 2>/dev/null || true
|
||||
setup_amber
|
||||
restart_zapstore
|
||||
sign_in
|
||||
@@ -420,21 +573,49 @@ ensure_signed_in() {
|
||||
|
||||
main() {
|
||||
log "═══ TEST-003: Bulk Update Flow ═══"
|
||||
|
||||
command -v adb >/dev/null 2>&1 || { echo "ERROR: adb not found in PATH"; exit 1; }
|
||||
command -v maestro >/dev/null 2>&1 || { echo "ERROR: maestro not found in PATH"; exit 1; }
|
||||
|
||||
log "Stage window: $FROM_STAGE..$TO_STAGE"
|
||||
|
||||
discover_device
|
||||
setup_screen
|
||||
ensure_signed_in
|
||||
uninstall_apps
|
||||
# Only restart when we did full setup (pm clear); when already signed in, Zapstore stays open
|
||||
if [ "${ALREADY_SIGNED_IN:-0}" -eq 0 ]; then
|
||||
restart_zapstore
|
||||
prepare_stage_window_prereqs
|
||||
|
||||
if should_run_stage 0; then
|
||||
prepare_clean_state
|
||||
else
|
||||
log "── Skip Stage 0 (clean_state) ──"
|
||||
fi
|
||||
install_all_older
|
||||
verify_and_update
|
||||
verify_post_update
|
||||
|
||||
if should_run_stage 1; then
|
||||
ensure_signed_in
|
||||
else
|
||||
log "── Skip Stage 1 (auth) ──"
|
||||
fi
|
||||
|
||||
if should_run_stage 2; then
|
||||
install_all_older
|
||||
else
|
||||
log "── Skip Stage 2 (install_old) ──"
|
||||
fi
|
||||
|
||||
if ! should_run_stage 2 && ( should_run_stage 3 || should_run_stage 4 ); then
|
||||
capture_versions_as_before
|
||||
fi
|
||||
|
||||
if should_run_stage 3; then
|
||||
verify_and_update
|
||||
else
|
||||
log "── Skip Stage 3 (update_all) ──"
|
||||
fi
|
||||
|
||||
if should_run_stage 4; then
|
||||
verify_post_update
|
||||
else
|
||||
log "── Skip Stage 4 (post_verify) ──"
|
||||
fi
|
||||
|
||||
generate_report
|
||||
|
||||
if [ $FAILURE_COUNT -gt 0 ]; then
|
||||
|
||||
Reference in New Issue
Block a user