From e36dc7521148967b77fad590e3718b9a02b5cb89 Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Mon, 14 Sep 2026 14:37:51 -0300 Subject: [PATCH 1/4] docs: document journeys and the android cli for agents --- AGENTS.md | 92 +++++++++++++++++++++++++++ journeys/README.md | 150 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 journeys/README.md diff --git a/AGENTS.md b/AGENTS.md index 1a5913c006..5e274a4cad 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,6 +52,92 @@ just install just clean ``` +### Agent CLI (android) + +Agents can drive a connected emulator or device with the `android` CLI, which wraps the SDK tooling +and adds a semantic UI dump. It is not provisioned by this repo — install it if it is missing +(`curl -fsSL https://dl.google.com/android/cli/latest/darwin_arm64/install.sh | bash`), and discover +arguments with `--help` rather than memorizing them. + +```sh +# Emulators +android emulator list # AVD names; `start` requires one, it has no default +android emulator start Pixel_9 + +# Inspect the screen +android layout --pretty # flat JSON of on-screen elements +android layout --diff # only what changed, to keep context small +android screen capture -o shot.png # secondary; use when layout hits a WebView or animation +android screen capture --annotate -o shot.png # numbered boxes, for elements layout cannot name + +# Interact +adb shell input tap # use an element's `center` +``` + +`android layout` reports each element's `resource-id`, `text`, `content-desc`, `interactions`, +`bounds` and `center`. Compose `testTag`s land in `resource-id`. **The JSON keys are hyphenated, not +camelCase** — the `android-cli` skill's `references/interact.md` documents `resourceId` and +`contentDesc`, and a filter written against those names matches nothing. + +Prefer `android layout` over screenshots: it names elements by their test tag, and full-resolution +screenshots can exceed image size limits. + +## Journeys + +`journeys/` holds XML walkthroughs of app behaviour that an agent evaluates by driving a running +emulator or device — number pad caps, notification permission, widget flows, deeplinks, hardware +wallet pairing and transfers. Read [`journeys/README.md`](journeys/README.md) before running or +writing one; it has the format, the runner commands and the per-suite preconditions. + +- Journeys are **developer-assistance specs, not a QA gate**. Nothing in `.github/workflows` reads + `journeys/`; `ui-tests.yml` runs the instrumented tests and never touches them. They are + agent-evaluated and non-deterministic, so they belong on a manual, developer-triggered run rather + than a blocking gate. An agent runs one on request. +- A journey is **not the source of truth** for app behaviour, despite what the `android-cli` skill's + own `references/journeys.md` says. A journey that disagrees with the app is most likely stale. Say + what you found and update the journey; escalate only once you have separately confirmed the app is + wrong. A crash, exit or freeze is the exception — stop there and escalate. +- **PORT the journeys whenever a feature crosses to or from iOS.** If a change ships or touches a + journey under `journeys/`, the matching `bitkit-ios` PR carries it, and vice versa. A ported + feature without its journey is an incomplete port. +- KEEP the file name, `` and `` prose identical across the two repos so the + specs stay diffable. Change only what the platform forces. +- MATCH the iOS identifier string when adding a `testTag` a journey asserts on — the vocabulary is + deliberately shared (`N9`, `NRemove`, `SpendingAmountContinue`, `HardwareTransferSign`). Record any + name that cannot match in the identifier table in `journeys/README.md`. +- ADAPT rather than transcribe when a platform genuinely behaves differently, and say so in the + journey's `` and the suite README — never assert behaviour the platform does not have. +- SKIP a journey only when the feature does not exist on the other side, and record it under the + cross-platform table in `journeys/README.md` with what is missing. + +### Running a journey on both platforms + +A journey is a shared spec, so when a behaviour is meant to match iOS, run the same file on both +sides rather than reasoning about the difference. Android uses the `android` CLI above; iOS uses the +XcodeBuildMCP CLI against a `bitkit-ios` checkout: + +```sh +xcodebuildmcp simulator build-and-run # build, install, launch, capture logs +xcodebuildmcp simulator snapshot-ui # the `android layout` equivalent +xcodebuildmcp ui-automation tap --element-ref e12 # tap one ref from the latest snapshot +xcodebuildmcp ui-automation wait-for-ui --identifier SpendingAmount --predicate exists +``` + +`snapshot-ui` names elements by `accessibilityIdentifier` the way `android layout` names them by +`resource-id`, so a journey's tag assertions map onto both. The vocabulary really is shared — +Settings on both platforms agrees on `Tab-general`, `Tab-security`, `Tab-advanced`, `NavigationBack`, +`HeaderMenu`, `CurrenciesSettings`, `UnitSettings`, `WidgetsSettings` and `QuickpaySettings` — so a +comparison run is mostly signal and the rows that disagree stand out. + +A cross-platform Lightning payment is the sharpest single check that the two builds agree: take an +invoice from one side and pay it from the other (`xcrun simctl pbpaste ` after tapping Copy on +iOS, then the `lightning:` URI route above on Android). + +When the two platforms disagree on a journey, write down which it looks like — an intentional +platform difference, or something worth a closer look — in the journey's `` and the +suite README, on both sides, so the next reader does not rediscover it. A disagreement is a prompt to +investigate, not a bug report on its own. + ## Architecture Overview ### Tech Stack @@ -289,6 +375,12 @@ suspend fun getData(): Result = withContext(Dispatchers.IO) { - ALWAYS download device files to `.ai/{name}_{timestamp}/` when needed for debugging (e.g. `.ai/logs_1770671066/`) - To download: `adb shell "run-as to.bitkit.dev cat files/path/to/file" > .ai/folder_timestamp/filename` - ALWAYS try reading device logs automatically via adb BEFORE asking user to provide log files +- NEVER type long strings with `adb shell input text` — it silently drops characters (it lost 54 of a + 397-character invoice), and `adb shell cmd clipboard` is not implemented on the emulator image. + Hand an address or invoice to the app as a URI instead, which also skips the recipient screen: + `adb shell am start -a android.intent.action.VIEW -d "lightning:" to.bitkit.dev` +- For short strings that must be typed, enter digit groups and separators separately, then verify — + dotted strings such as host IPs are where the dropping shows up first ### Architecture Guidelines diff --git a/journeys/README.md b/journeys/README.md new file mode 100644 index 0000000000..79c6ecf89e --- /dev/null +++ b/journeys/README.md @@ -0,0 +1,150 @@ +# Journeys + +A journey is an XML-specified walkthrough of app behaviour, evaluated by an agent driving a running +emulator or device. They are developer-assistance specs: they give an agent a reliable route through +a flow so it can reproduce a bug, check a change by hand, or show you what a screen does today. + +**Journeys are not a QA gate.** Nothing in `.github/workflows` reads `journeys/` — `ui-tests.yml` +runs the instrumented tests and never touches this directory. They are agent-evaluated and +non-deterministic, which is why they belong on a manual, developer-triggered run rather than a +blocking CI gate. An agent runs one on request. + +**A journey is not the source of truth.** The `android` CLI ships its own journey documentation +(`references/journeys.md` in the `android-cli` skill) which says the opposite — "the journey XML is +the source of truth; if the app disagrees with the journey, the app has failed". That rule does not +apply here. A journey that no longer matches the app is most likely **stale**: the corpus is +maintained by hand alongside two codebases and drifts. Say what you found, update the journey, and +escalate only once you have separately confirmed the app is wrong. Never quietly rewrite a journey +to describe a behaviour you have not checked — that swaps one wrong description for another. + +The one exception: if the app crashes, exits or freezes, evaluation stops there, and that *is* worth +escalating. + +## Format + +```xml + + What this proves, and the preconditions needed to prove it. + + Tap the Spending balance card on the home screen + Verify the spending amount screen (tag "SpendingAmount") is visible + + +``` + +- Evaluate `` elements in order, and report each one. An action that does not hold is worth + reporting as-is — it may be a stale step as easily as a real problem. +- An action beginning with "check" or "verify" is an expectation about the **current** screen — + inspect it, do not scroll or interact to satisfy it. +- An action that specifies several interactions is split into sub-actions and evaluated individually. +- An action may also name a shell command to run (`adb shell am start …`); run it as written. +- If an interaction cannot be performed as written, say so and stop rather than improvising a route + around it — the point is to find out where the written route stopped matching the app. + +## Running a journey + +Drive the device with the `android` CLI (see the Agent CLI section in `AGENTS.md`): + +```bash +android emulator list # AVD names; `start` requires one, it has no default +android emulator start Pixel_9 # or `android run` against a connected device +android layout --pretty # flat JSON of on-screen elements +android layout --diff # only what changed, to keep context small +android screen capture -o shot.png # secondary; use when layout hits a WebView or animation +adb shell input tap # tap an element's `center` +``` + +`android layout` reports each element's `resource-id`, `text`, `content-desc`, `interactions`, +`bounds` and `center`. Compose `testTag`s land in `resource-id`, which is what the journeys assert +on. **The JSON keys are hyphenated, not camelCase** — the skill's `references/interact.md` documents +`resourceId` and `contentDesc`, and a runner filtering on those finds nothing. + +Prefer `android layout` over screenshots: it names elements by their test tag, and full-resolution +screenshots can exceed image size limits. + +**Do not type long strings.** `adb shell input text` silently drops characters — it lost 54 of a +397-character invoice in testing — and `adb shell cmd clipboard` is not implemented on the emulator +image. Hand an address or invoice to the app as a URI instead, which also skips the recipient screen: + +```bash +adb shell am start -a android.intent.action.VIEW -d "lightning:" to.bitkit.dev +``` + +For short strings that must be typed, enter digit groups and separators separately and verify after — +dotted strings such as host IPs are where the dropping shows up first. + +## Backend preconditions + +The dev flavor targets the **staging regtest** backend (`Env.kt` points regtest at +`*.stag0.blocktank.to`), so most journeys need nothing running locally beyond the app. Fund and mine +through the `lsp` helper at the repo root, which talks to the same staging LSP: + +```bash +./lsp POST /regtest/chain/deposit '{"address":"","amountSat":100000}' +./lsp POST /regtest/chain/mine '{"count":3}' +``` + +`deposit` prints the funding txid; **`mine` prints nothing on success** and signals only through its +exit status, so an empty response there is not a failure. Give the wallet ~20s to sync. + +Fund a wallet before any amount journey — with a zero balance the caps fall back to the global +maximum and the journeys pass for the wrong reason. Per-suite preconditions (Trezor emulator, Pubky +fixtures, push notifications) live in each suite's README. + +## Suites + +| Suite | Journeys | Notes | +| --- | --- | --- | +| [amount-limits](amount-limits) | 4 | Number pad caps on all four amount screens | +| [cjit-notifications](cjit-notifications) | 3 | CJIT channel-ready notifications; needs FCM push | +| [deeplinks](deeplinks) | 2 | `bitkit://screen/…` and sheet routing behind the dev-mode gate; no README | +| [hardware-wallet](hardware-wallet) | 17 | Trezor over USB; needs the Trezor emulator | +| [notification-permission](notification-permission) | 4 | Background-setup toggles | +| [payment-requests](payment-requests) | 2 | Requires a linked fixture issuer; rejected shapes are unit fixtures | +| [pubky-marketplace](pubky-marketplace) | 1 | Two-wallet Paykit marketplace payment; integration fixture required | +| [widgets](widgets) | 2 | Needs no backend — the quickest way to see the loop work; no README | + +## Cross-platform + +These journeys are also carried by [`bitkit-ios/journeys`](https://github.com/synonymdev/bitkit-ios/tree/main/journeys), +which aims to keep the same file names, journey names and `` prose so the two sides stay +diffable. +Only the mechanics differ: `android`/`adb` becomes `xcodebuildmcp`, and Compose `testTag`s become +`accessibilityIdentifier`s. `AGENTS.md` has the command equivalents and the rules for porting. + +Known differences in the corpus, as of the iOS port (synonymdev/bitkit-ios#691): + +| Here | On iOS | +| --- | --- | +| `cjit-notifications/cjit-foreground-service-notification.xml` | `cjit-background-notification.xml`, with the thousands-separator assertions dropped — the notification extension never formats an amount | +| `notification-permission/toggle-off-opens-system-settings.xml` | `toggle-off-and-system-settings-route.xml` — iOS does not deep link into system settings, so it asserts the real route in through Settings ▸ Notifications | +| `hardware-wallet/usb-reconnect.xml` | `reconnect.xml` — over Bridge, since iOS cannot do WebUSB | +| `hardware-wallet/receive-onchain.xml`, `hardware-wallet/send-onchain.xml` | not ported | +| `payment-requests/requested-resolution-failure.xml` | not ported | +| `deeplinks/*` | not ported — iOS registers the `bitkit` scheme but has no screen or sheet router | +| — | `hardware-wallet/transfer-to-spending-over-max.xml` exists only on iOS | + +### Identifiers + +The vocabulary is deliberately shared, and mostly matches: the whole number pad (`N0`–`N9`, `N000`, +`NDecimal`, `NRemove`), `SpendingAmount*`, `SpendingAdvanced*`, `External*`, `Hardware*`, `Widget*`, +and Settings (`Tab-general`, `Tab-security`, `Tab-advanced`, `NavigationBack`, `HeaderMenu`, +`CurrenciesSettings`, `UnitSettings`, `WidgetsSettings`, `QuickpaySettings`). + +| Concept | Android testTag | iOS accessibilityIdentifier | +| --- | --- | --- | +| Send amount screen | `send_amount_screen` | `SendAmount` | +| Send available balance | `AvailableAmount` **and** `available_balance` | `AvailableAmount` | +| Send max | `SendAmountMax` | *(no button — tap `AvailableAmount`)* | +| External amount available | — | `ExternalAmountAvailable` | +| Background payments setting row | `BackgroundPaymentSettings` | `NotificationsSettings` | +| Payment Request row | `PaymentRequestRow-` | `PaymentRequestRow----` | + +Two of those are unreconciled rather than intentional: the Send screen emitting both +`AvailableAmount` and `available_balance`, and the background-payments row name. Settling either is a +code change on one side, not a journey change. + +One asymmetry worth knowing when comparing: Android builds `Tab-*` from the enum name +(`CustomTabRowWithSpacing`), so `Tab-all` is stable in any locale, while iOS derives it from the +tab's display name and becomes `Tab-todas` in Spanish. Journeys naming a `Tab-*` identifier assume an +English device for iOS's sake. From bcfa374e3d50226a3d6f85d87ab5cbd890fa1bff Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Mon, 14 Sep 2026 14:55:26 -0300 Subject: [PATCH 2/4] docs: correct journey docs from a live two-platform run Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01KavTcT18zALriWtzY57GTR --- AGENTS.md | 4 ++- journeys/README.md | 44 +++++++++++++++++++++++++++--- journeys/widgets/widgets-intro.xml | 10 +++++-- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5e274a4cad..e2ec1c5d13 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -80,7 +80,9 @@ camelCase** — the `android-cli` skill's `references/interact.md` documents `re `contentDesc`, and a filter written against those names matches nothing. Prefer `android layout` over screenshots: it names elements by their test tag, and full-resolution -screenshots can exceed image size limits. +screenshots can exceed image size limits. It does not see everything, though — toasts never appear +in it, some tiles carry no text, and an element can be missing from one dump and present in the +next. `journeys/README.md` lists what needs a screenshot instead. ## Journeys diff --git a/journeys/README.md b/journeys/README.md index 79c6ecf89e..403c5e2309 100644 --- a/journeys/README.md +++ b/journeys/README.md @@ -62,6 +62,19 @@ on. **The JSON keys are hyphenated, not camelCase** — the skill's `references/ Prefer `android layout` over screenshots: it names elements by their test tag, and full-resolution screenshots can exceed image size limits. +**Not everything on screen reaches `android layout`.** Verified while running these journeys: + +- **Toasts never appear.** The "Insufficient balance" warning the amount journeys assert on is + visible only in a screenshot. Capture one immediately after the rejected keypress — the toast + lasts about 1.5s. +- **Widget gallery tiles carry no text.** The Add Widget sheet lists `WidgetListItem-price`, + `WidgetListItem-weather` and so on with empty `text`, so "verify Bitcoin Price and Bitcoin Weather + are visible" needs a screenshot. Assert the identifier instead where the journey allows it. +- **Exposure is not stable between dumps.** `HeaderMenu` and `ProfileButton` were absent from one + home-screen dump and present in the next, with no navigation in between. If an element a journey + names is missing, dump again before concluding it is gone, then fall back to + `android screen capture --annotate` and `android screen resolve` to tap it by coordinate. + **Do not type long strings.** `adb shell input text` silently drops characters — it lost 54 of a 397-character invoice in testing — and `adb shell cmd clipboard` is not implemented on the emulator image. Hand an address or invoice to the app as a URI instead, which also skips the recipient screen: @@ -70,6 +83,12 @@ image. Hand an address or invoice to the app as a URI instead, which also skips adb shell am start -a android.intent.action.VIEW -d "lightning:" to.bitkit.dev ``` +`am` echoes the intent back with the data redacted — `dat=bitcoin:` with nothing after the scheme. +That is `Uri.toSafeString()` hiding the rest, not a truncated argument; the full URI is delivered. +To confirm the app received it, read the app log rather than the `am` output: a good delivery logs +`Received deeplink`, `Queuing 'deeplink' scan` and `Starting scan from 'deeplink'`, and an invalid +address then fails with `Failed to decode scan data`. + For short strings that must be typed, enter digit groups and separators separately and verify after — dotted strings such as host IPs are where the dropping shows up first. @@ -107,10 +126,11 @@ fixtures, push notifications) live in each suite's README. ## Cross-platform These journeys are also carried by [`bitkit-ios/journeys`](https://github.com/synonymdev/bitkit-ios/tree/main/journeys), -which aims to keep the same file names, journey names and `` prose so the two sides stay -diffable. -Only the mechanics differ: `android`/`adb` becomes `xcodebuildmcp`, and Compose `testTag`s become -`accessibilityIdentifier`s. `AGENTS.md` has the command equivalents and the rules for porting. +which keeps the same file names, journey names and step sequence so the two sides stay diffable. +The prose is not byte-identical: each side annotates its own identifier vocabulary, so a step reads +`(testTag "N9")` here and `(id "N9")` on iOS, and a platform sometimes adds a note of its own. Diff +for the shape of the journey, not for equality. `AGENTS.md` has the command equivalents and the +rules for porting. Known differences in the corpus, as of the iOS port (synonymdev/bitkit-ios#691): @@ -124,6 +144,18 @@ Known differences in the corpus, as of the iOS port (synonymdev/bitkit-ios#691): | `deeplinks/*` | not ported — iOS registers the `bitkit` scheme but has no screen or sheet router | | — | `hardware-wallet/transfer-to-spending-over-max.xml` exists only on iOS | +### Running one on iOS + +Two mechanics differ from the Android runner, both hit while checking this corpus: + +- **`elementRef`s expire on a timer**, not only when the layout changes. A tap against a ref from a + snapshot taken a minute earlier fails with "the runtime UI snapshot for this simulator has + expired". Snapshot and tap in the same step. +- **`snapshot-ui` omits controls that are still tappable.** The receive screen's Copy button is not + in its target list, but `wait-for-ui --identifier ReceiveCopyQR --predicate exists` returns it + *with a usable ref*. Use that to reach an element the snapshot does not list, rather than + concluding it is gone. + ### Identifiers The vocabulary is deliberately shared, and mostly matches: the whole number pad (`N0`–`N9`, `N000`, @@ -138,6 +170,10 @@ and Settings (`Tab-general`, `Tab-security`, `Tab-advanced`, `NavigationBack`, ` | Send max | `SendAmountMax` | *(no button — tap `AvailableAmount`)* | | External amount available | — | `ExternalAmountAvailable` | | Background payments setting row | `BackgroundPaymentSettings` | `NotificationsSettings` | +| Send over-max toast | — *(no tag; assert it from a screenshot)* | `SendAmountExceededToast` | +| Widgets intro screen container | — | `WidgetsOnboarding` | +| Home suggestion cards | `Suggestion-` | — *(cards expose no identifier)* | +| Receive QR copy button | `ReceiveCopyQR` | `ReceiveCopyQR` *(absent from `snapshot-ui` targets; see below)* | | Payment Request row | `PaymentRequestRow-` | `PaymentRequestRow----` | Two of those are unreconciled rather than intentional: the Send screen emitting both diff --git a/journeys/widgets/widgets-intro.xml b/journeys/widgets/widgets-intro.xml index c797ca900f..732cce4c78 100644 --- a/journeys/widgets/widgets-intro.xml +++ b/journeys/widgets/widgets-intro.xml @@ -1,5 +1,11 @@ - Precondition: onboarded dev wallet, widgets enabled, widgets intro unseen. + + Precondition: onboarded dev wallet, widgets enabled, widgets intro unseen. + + A saved widget takes a fixed position in the widget set rather than being appended last — + verified identically on Android and iOS against a set that already held other widgets — so this + journey asserts only that it is present. + Tap the menu icon Tap "Widgets" @@ -11,6 +17,6 @@ Verify that the bottom sheet navigates to "Bitcoin Weather" and "Save Widget" is visible Tap "Save Widget" Verify that the wallet overview widgets section is visible - Verify that "Bitcoin Weather" appears as the last widget in the current widget set + Verify that "Bitcoin Weather" appears in the current widget set From 7df19fb5f380e34b58d51f45ea87768c05005c3b Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Mon, 14 Sep 2026 14:57:46 -0300 Subject: [PATCH 3/4] docs: make android cli install platform-aware Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01KavTcT18zALriWtzY57GTR --- AGENTS.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e2ec1c5d13..8d88e4f045 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,9 +55,18 @@ just clean ### Agent CLI (android) Agents can drive a connected emulator or device with the `android` CLI, which wraps the SDK tooling -and adds a semantic UI dump. It is not provisioned by this repo — install it if it is missing -(`curl -fsSL https://dl.google.com/android/cli/latest/darwin_arm64/install.sh | bash`), and discover -arguments with `--help` rather than memorizing them. +and adds a semantic UI dump. It is not provisioned by this repo — install it if it is missing, from +`https://dl.google.com/android/cli/latest//install.sh` where `` is one of +`darwin_arm64`, `darwin_x86_64` or `linux_x86_64`: + +```sh +curl -fsSL https://dl.google.com/android/cli/latest/darwin_arm64/install.sh | bash +``` + +On Windows the installer is a `.cmd` instead: +`curl -fsSL https://dl.google.com/android/cli/latest/windows_x86_64/install.cmd -o "%TEMP%\i.cmd" && "%TEMP%\i.cmd"`. + +Discover arguments with `--help` rather than memorizing them. ```sh # Emulators From 1a19983ab3410c305217f8192c6c6fc611049a8d Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Tue, 15 Sep 2026 07:31:37 -0300 Subject: [PATCH 4/4] test: restore widgets-intro last-widget assertion Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LJXzGrHwhH44d9eVM6RQUq --- journeys/widgets/widgets-intro.xml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/journeys/widgets/widgets-intro.xml b/journeys/widgets/widgets-intro.xml index 732cce4c78..7a37a0d2b0 100644 --- a/journeys/widgets/widgets-intro.xml +++ b/journeys/widgets/widgets-intro.xml @@ -1,10 +1,9 @@ - Precondition: onboarded dev wallet, widgets enabled, widgets intro unseen. - - A saved widget takes a fixed position in the widget set rather than being appended last — - verified identically on Android and iOS against a set that already held other widgets — so this - journey asserts only that it is present. + Precondition: onboarded dev wallet, widgets enabled, widgets intro unseen, and Bitcoin Weather + NOT already in the widget set — re-saving a widget that is already there keeps its original + position instead of appending, so the final assertion fails for the wrong reason. Delete it from + Add Widget first if present. Tap the menu icon @@ -17,6 +16,6 @@ Verify that the bottom sheet navigates to "Bitcoin Weather" and "Save Widget" is visible Tap "Save Widget" Verify that the wallet overview widgets section is visible - Verify that "Bitcoin Weather" appears in the current widget set + Verify that "Bitcoin Weather" appears as the last widget in the current widget set