From d26a3c85c9145da4d1ba8fb485f3d193b190ffc2 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 10 Sep 2026 20:46:30 +0200 Subject: [PATCH 1/3] chore: add journeys index check --- .github/workflows/journeys.yml | 26 + journeys/index.json | 855 +++++++++++++++++++++++++++++++++ scripts/journeys_index.py | 151 ++++++ 3 files changed, 1032 insertions(+) create mode 100644 .github/workflows/journeys.yml create mode 100644 journeys/index.json create mode 100755 scripts/journeys_index.py diff --git a/.github/workflows/journeys.yml b/.github/workflows/journeys.yml new file mode 100644 index 0000000000..fa9133bf2f --- /dev/null +++ b/.github/workflows/journeys.yml @@ -0,0 +1,26 @@ +name: Journeys + +on: + push: + branches: [master] + pull_request: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + index: + name: Check journeys index + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Check journeys index + run: python3 scripts/journeys_index.py --check diff --git a/journeys/index.json b/journeys/index.json new file mode 100644 index 0000000000..a0547383a9 --- /dev/null +++ b/journeys/index.json @@ -0,0 +1,855 @@ +{ + "generatedBy": "python3 scripts/journeys_index.py", + "journeys": [ + { + "file": "journeys/amount-limits/external-amount-over-max.xml", + "name": "external node amount over max is blocked", + "identifiers": { + "FundManual": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/FundingAdvancedScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/FundingScreen.kt" + ], + "NodeIdInput": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt" + ], + "HostInput": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt", + "app/src/main/java/to/bitkit/ui/settings/advanced/ElectrumConfigScreen.kt" + ], + "PortInput": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt", + "app/src/main/java/to/bitkit/ui/settings/advanced/ElectrumConfigScreen.kt" + ], + "ExternalContinue": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt" + ], + "ExternalAmount": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalAmountScreen.kt" + ], + "N9": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" + ], + "ExternalAmountNumberField": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalAmountScreen.kt" + ], + "NRemove": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" + ] + }, + "screens": [ + "funding options screen", + "external node amount screen" + ] + }, + { + "file": "journeys/amount-limits/send-amount-over-balance.xml", + "name": "send amount over balance is blocked", + "identifiers": { + "Send": [ + "app/src/main/java/to/bitkit/ui/components/TabBar.kt" + ], + "RecipientManual": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendRecipientScreen.kt" + ], + "RecipientInput": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAddressScreen.kt" + ], + "AddressContinue": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAddressScreen.kt" + ], + "send_amount_screen": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" + ], + "available_balance": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" + ], + "N9": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" + ], + "SendNumberField": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" + ], + "NRemove": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" + ] + }, + "screens": [ + "Send amount screen" + ] + }, + { + "file": "journeys/amount-limits/transfer-spending-advanced-over-max.xml", + "name": "receiving capacity amount over max is blocked", + "identifiers": { + "SpendingAmount": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "SpendingAmountAvailable": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "SpendingAmountQuarter": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "SpendingAmountContinue": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "SpendingAdvanced": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAdvancedScreen.kt" + ], + "N9": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" + ], + "SpendingAdvancedNumberField": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAdvancedScreen.kt" + ], + "NRemove": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" + ] + }, + "screens": [ + "home screen", + "transfer intro screen", + "spending amount screen", + "confirm screen", + "receiving capacity screen" + ] + }, + { + "file": "journeys/amount-limits/transfer-spending-over-max.xml", + "name": "transfer to spending amount over max is blocked", + "identifiers": { + "SpendingAmount": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "SpendingAmountAvailable": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "N9": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" + ], + "SpendingAmountNumberField": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "NRemove": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" + ] + }, + "screens": [ + "home screen", + "transfer intro screen", + "spending amount screen" + ] + }, + { + "file": "journeys/cjit-notifications/cjit-foreground-service-notification.xml", + "name": "cjit notification via foreground service is single and formatted", + "identifiers": {}, + "screens": [ + "wallet home screen", + "received-transaction sheet" + ] + }, + { + "file": "journeys/cjit-notifications/cjit-push-single-notification.xml", + "name": "cjit push notification is delivered exactly once", + "identifiers": {}, + "screens": [] + }, + { + "file": "journeys/cjit-notifications/non-cjit-channel-no-payment-notification.xml", + "name": "non-cjit channel opening shows no payment notification", + "identifiers": { + "SpendingBalanceReadyToast": [ + "app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt" + ] + }, + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/deeplinks/screen-deeplink.xml", + "name": "screen deeplink", + "identifiers": {}, + "screens": [ + "Settings screen", + "log detail screen" + ] + }, + { + "file": "journeys/deeplinks/sheet-deeplink.xml", + "name": "sheet deeplink", + "identifiers": {}, + "screens": [ + "Send sheet" + ] + }, + { + "file": "journeys/hardware-wallet/activity-blue-icons.xml", + "name": "Hardware Wallet Activity Blue Icons", + "identifiers": {}, + "screens": [ + "wallet home screen", + "detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/activity-detail-hw-tags.xml", + "name": "Hardware Wallet Activity Tags And Inputs Outputs", + "identifiers": {}, + "screens": [ + "wallet home screen", + "activity detail screen", + "Activity Explorer screen" + ] + }, + { + "file": "journeys/hardware-wallet/connect-flow.xml", + "name": "Hardware Wallet Connect Flow From Settings", + "identifiers": { + "HardwareWalletsScreen": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "AddHardwareWallet": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "HardwareWalletIntroScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" + ], + "HardwareWalletIntroContinue": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" + ], + "HardwareWalletSearchingScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwSearchingSheet.kt" + ], + "HardwareWalletFoundScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" + ], + "HardwareWalletFoundConnect": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" + ], + "HardwareWalletPairedScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ], + "HardwareWalletLabelInput": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ], + "HardwareWalletPairedFinish": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ] + }, + "screens": [ + "Hardware Wallets screen" + ] + }, + { + "file": "journeys/hardware-wallet/connect-home-tile.xml", + "name": "Hardware Wallet Connect and Home Tile", + "identifiers": { + "HardwareWalletScreen": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ] + }, + "screens": [ + "wallet home screen", + "hardware wallet detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/detail-overview.xml", + "name": "Hardware Wallet Detail Overview", + "identifiers": { + "HardwareWalletScreen": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "HardwareTransferToSpending": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "HardwareTransferAmount": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "RemoveHardwareWallet": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ] + }, + "screens": [ + "wallet home screen", + "hardware wallet detail screen", + "transfer amount screen", + "activity detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-duplicate.xml", + "name": "Hardware Wallet Passphrase Duplicate", + "identifiers": { + "AddHardwareWallet": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "HardwareWalletIntroContinue": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" + ], + "HardwareWalletFoundConnect": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" + ], + "HardwareWalletPairedPassphrase": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ], + "HardwareWalletPassphraseInput": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" + ], + "HardwareWalletPassphraseContinue": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" + ], + "HardwareWalletPassphraseScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" + ] + }, + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-pairing.xml", + "name": "Hardware Wallet Passphrase Pairing", + "identifiers": { + "AddHardwareWallet": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "HardwareWalletIntroContinue": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" + ], + "HardwareWalletFoundScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" + ], + "HardwareWalletFoundConnect": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" + ], + "HardwareWalletPairedScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ], + "HardwareWalletPairedPassphrase": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ], + "HardwareWalletPairedFinish": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ], + "HardwareWalletPassphraseScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" + ], + "HardwareWalletPassphraseContinue": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" + ], + "HardwareWalletPassphraseInput": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" + ], + "HardwareWalletPassphrasePairedScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphrasePairedSheet.kt" + ], + "HardwareWalletLabelInput": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ], + "HardwareWalletScreen": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ] + }, + "screens": [ + "wallet home screen", + "hardware wallet detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-settings-remove.xml", + "name": "Hardware Wallet Passphrase Settings And Remove", + "identifiers": { + "HardwareWalletsScreen": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "RenameHardwareWalletInput": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "RenameHardwareWalletSave": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ] + }, + "screens": [ + "rename sheet", + "Hardware Wallets screen", + "wallet home screen", + "detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-transfer-to-spending.xml", + "name": "Hardware Wallet Passphrase Transfer To Spending", + "identifiers": { + "HardwareWalletScreen": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "HardwareTransferToSpending": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "HardwareTransferAmountQuarter": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "HardwareTransferAmountContinue": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "HardwareTransferSign": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" + ], + "HardwareTransferOpenTrezorConnect": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" + ], + "HardwareTransferSigned": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignedScreen.kt" + ], + "HwTransferPassphraseSheet": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt" + ], + "HwTransferPassphraseInput": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt" + ], + "HwTransferPassphraseContinue": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt" + ] + }, + "screens": [ + "wallet home screen", + "detail screen", + "sign screen", + "transaction signed screen", + "Processing Payment screen", + "home screen", + "passphrase sheet" + ] + }, + { + "file": "journeys/hardware-wallet/receive-onchain.xml", + "name": "Hardware Wallet On-chain Receive", + "identifiers": { + "Receive": [ + "app/src/main/java/to/bitkit/ui/components/TabBar.kt" + ], + "ReceiveScreen": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveSheet.kt" + ], + "QRCode": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt" + ], + "ShowDetails": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt" + ], + "ReceiveHardwareAddress": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt" + ], + "HardwareVerifyAddress": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt" + ] + }, + "screens": [ + "wallet home screen", + "Receive sheet" + ] + }, + { + "file": "journeys/hardware-wallet/send-onchain.xml", + "name": "Hardware Wallet On-chain Send", + "identifiers": { + "Send": [ + "app/src/main/java/to/bitkit/ui/components/TabBar.kt" + ], + "RecipientManual": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendRecipientScreen.kt" + ], + "RecipientInput": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAddressScreen.kt" + ], + "AddressContinue": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAddressScreen.kt" + ], + "send_amount_screen": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" + ], + "AssetButton-switch": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" + ], + "ContinueAmount": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveAmountScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" + ], + "SendConfirmAssetButton": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" + ], + "HardwareSendAmount": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/HwSendSignScreen.kt" + ], + "HardwareSendAddress": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/HwSendSignScreen.kt" + ], + "HardwareSendOpenTrezorConnect": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/HwSendSignScreen.kt" + ], + "SendSuccess": [ + "app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt" + ] + }, + "screens": [ + "wallet home screen", + "Send amount screen", + "Send review screen", + "hardware sign screen", + "success screen" + ] + }, + { + "file": "journeys/hardware-wallet/settings-hardware-wallets.xml", + "name": "Hardware Wallet Settings", + "identifiers": { + "HardwareWalletsScreen": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "RenameHardwareWalletInput": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "RenameHardwareWalletSave": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "AddHardwareWallet": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "HardwareWalletIntroScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" + ] + }, + "screens": [ + "Hardware Wallets screen", + "Rename Hardware Wallet bottom sheet" + ] + }, + { + "file": "journeys/hardware-wallet/suggestion-intro-sheet.xml", + "name": "Hardware Wallet Suggestion Card and Intro Sheet", + "identifiers": { + "HardwareWalletIntroScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" + ], + "HardwareWalletIntroContinue": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" + ], + "HardwareWalletSearchingScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwSearchingSheet.kt" + ], + "HardwareWalletSearchingCancel": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwSearchingSheet.kt" + ], + "HardwareWalletFoundScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" + ], + "HardwareWalletFoundConnect": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" + ], + "HardwareWalletPairedScreen": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ], + "HardwareWalletLabelInput": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ], + "HardwareWalletPairedFinish": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" + ] + }, + "screens": [ + "wallet home screen", + "home screen" + ] + }, + { + "file": "journeys/hardware-wallet/transfer-to-spending-max-lsp-cap.xml", + "name": "Hardware Wallet Transfer To Spending MAX LSP Cap", + "identifiers": { + "HardwareWalletScreen": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "HardwareTransferToSpending": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "HardwareTransferAmount": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "HardwareTransferAmountMax": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "HardwareTransferAmountContinue": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "HardwareTransferSign": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" + ], + "HardwareTransferOpenTrezorConnect": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" + ], + "HardwareTransferSigned": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignedScreen.kt" + ] + }, + "screens": [ + "wallet home screen", + "hardware wallet detail screen", + "transfer amount screen", + "sign screen", + "transaction signed screen" + ] + }, + { + "file": "journeys/hardware-wallet/transfer-to-spending-node-warmup.xml", + "name": "Hardware Wallet Transfer To Spending Node Warmup", + "identifiers": { + "HardwareTransferToSpending": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "HardwareTransferAmountQuarter": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "HardwareTransferAmountContinue": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "HardwareTransferSign": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" + ] + }, + "screens": [ + "wallet home screen", + "transfer amount screen", + "amount screen", + "sign screen" + ] + }, + { + "file": "journeys/hardware-wallet/transfer-to-spending.xml", + "name": "Hardware Wallet Transfer To Spending", + "identifiers": { + "HardwareWalletScreen": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "HardwareTransferToSpending": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "HardwareTransferAmount": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "HardwareTransferAmountQuarter": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "HardwareTransferAmountContinue": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" + ], + "HardwareTransferSign": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" + ], + "HardwareTransferOpenTrezorConnect": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" + ], + "HardwareTransferSigned": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignedScreen.kt" + ] + }, + "screens": [ + "wallet home screen", + "hardware wallet detail screen", + "transfer amount screen", + "sign screen", + "transaction signed screen", + "detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/usb-reconnect.xml", + "name": "Hardware Wallet USB Reconnect", + "identifiers": {}, + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/notification-permission/receive-cjit-confirm-notification-toggle.xml", + "name": "receive cjit confirm toggle requests notification permission", + "identifiers": { + "ReceiveConfirmNotificationSwitch": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt" + ] + }, + "screens": [ + "home screen", + "Receive sheet", + "amount screen", + "confirm screen" + ] + }, + { + "file": "journeys/notification-permission/receive-cjit-liquidity-notification-toggle.xml", + "name": "receive cjit liquidity toggle requests notification permission", + "identifiers": { + "ReceiveLiquidityNotificationSwitch": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveLiquidityScreen.kt" + ] + }, + "screens": [ + "home screen", + "Receive sheet", + "amount screen", + "confirm screen", + "liquidity screen" + ] + }, + { + "file": "journeys/notification-permission/toggle-off-opens-system-settings.xml", + "name": "notification toggle off opens system settings", + "identifiers": { + "ReceiveConfirmNotificationSwitch": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt" + ] + }, + "screens": [ + "home screen", + "Receive sheet", + "amount screen", + "confirm screen", + "Android system notification settings screen" + ] + }, + { + "file": "journeys/notification-permission/transfer-spending-confirm-notification-toggle.xml", + "name": "transfer spending confirm toggle requests notification permission", + "identifiers": { + "SpendingAmount": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "SpendingAmountAvailable": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "SpendingAmountContinue": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "SpendingConfirmNotificationSwitch": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingConfirmScreen.kt" + ] + }, + "screens": [ + "home screen", + "transfer intro screen", + "spending amount screen", + "spending confirm screen" + ] + }, + { + "file": "journeys/payment-requests/issuer-interoperability.xml", + "name": "Paykit Issuer Interoperability", + "identifiers": { + "PaymentRequestConfirm": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" + ], + "PaymentRequestsBell": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt" + ], + "PaymentRequestsSheet": [ + "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" + ], + "PaymentRequestRow-71300000-0000-4000-8000-000000000001": [ + "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" + ], + "PaymentRequestPay-71300000-0000-4000-8000-000000000001": [ + "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" + ], + "SendConfirmToggleDetails": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" + ], + "ReviewContactRecipient": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" + ] + }, + "screens": [ + "Payment Request confirmation screen", + "incoming Payment Requests sheet" + ] + }, + { + "file": "journeys/pubky-marketplace/wallet-leg.xml", + "name": "pubky marketplace wallet leg", + "identifiers": { + "PubkyAuthWatchOnlyConsent": [ + "app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt" + ], + "PubkyAuthWatchOnlyApprove": [ + "app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt" + ], + "PubkyAuthAuthorize": [ + "app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt" + ], + "PubkyAuthOK": [ + "app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt" + ], + "ContactPaymentsToggle": [ + "app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt" + ], + "Contact_": [ + "app/src/main/java/to/bitkit/ui/screens/contacts/ContactsScreen.kt" + ], + "Contact_": [ + "app/src/main/java/to/bitkit/ui/screens/contacts/ContactsScreen.kt" + ], + "ReviewAmount": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" + ], + "PaymentRequestsBell": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt" + ], + "PaymentRequestsSheet": [ + "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" + ], + "PaymentRequestRow-": [ + "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" + ], + "PaymentRequestPay-": [ + "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" + ], + "ReviewContactRecipient": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" + ], + "GRAB": [ + "app/src/main/java/to/bitkit/ui/components/SwipeToConfirm.kt" + ], + "SendSuccess": [ + "app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt" + ], + "ActivityAmount": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt" + ], + "ActivityTxDetails": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt" + ] + }, + "screens": [ + "watch-only consent screen", + "authorization screen", + "incoming requests sheet" + ] + }, + { + "file": "journeys/widgets/add-widgets-flow.xml", + "name": "add widgets flow", + "identifiers": {}, + "screens": [ + "Add Widget bottom sheet" + ] + }, + { + "file": "journeys/widgets/widgets-intro.xml", + "name": "widgets intro", + "identifiers": {}, + "screens": [ + "Add Widget bottom sheet" + ] + } + ] +} diff --git a/scripts/journeys_index.py b/scripts/journeys_index.py new file mode 100755 index 0000000000..e6d2146977 --- /dev/null +++ b/scripts/journeys_index.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +"""Generate journeys/index.json: what each journey's actions name. + +Per journeys/**/*.xml file the index records the file, the , each identifier its +actions name with the source files that declare it, and the screens its actions name. + +An identifier resolves to the files with a testTag("…") call for it; failing that, to the files +with a string literal equal to it (a named argument, a constant, a conditional branch); failing +that, to the files with the most specific string template that can produce it ("N$text"). In a +template with fewer than three characters of its own, an interpolation matches no capital letters, +so "N$text" produces N9 but not NavigationBack. + + python3 scripts/journeys_index.py write journeys/index.json + python3 scripts/journeys_index.py --check fail when journeys/index.json does not match the tree + +Both modes fail when a journey names an identifier that no source file declares. +""" + +import argparse +import difflib +import itertools +import json +import re +import sys +import xml.etree.ElementTree as ET +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +JOURNEYS_DIR = "journeys" +INDEX_FILE = "journeys/index.json" +COMMAND = "python3 scripts/journeys_index.py" + +# Platform: an action names an identifier as testTag "Name"; Kotlin sources declare it. +IDENTIFIER_MENTION = re.compile(r'\btestTag "([^"]+)"') +SOURCE_DIRS = ("app/src/main", "app/src/debug", "app/src/release") +SOURCE_SUFFIX = ".kt" +STRING_LITERAL = re.compile(r'"((?:[^"\\\n$]|\\.|\$\{[^{}\n]*\}|\$)*)"') +INTERPOLATION = re.compile(r"\$\{[^{}\n]*\}|\$[A-Za-z_]\w*") +DECLARATION_CALL = re.compile(r"\btestTag\(\s*" + STRING_LITERAL.pattern + r"\s*\)") + +SCREEN_WORD = r'(?:"[^"]+"|(?!(?:a|an|and|at|for|from|in|into|its|of|on|or|the|to|with)\b)[\w-]+)' +SCREEN_MENTION = re.compile(rf"\b(?:[Tt]he|[Aa]n?|[Ii]ts)\s+({SCREEN_WORD}(?:\s+{SCREEN_WORD}){{0,3}}?)\s+(screen|sheet)\b") +GENERIC_SCREEN_WORDS = {"bottom", "current", "entire", "first", "last", "new", "next", "other", "previous", "same", "whole"} + + +def scan_sources(): + calls, literals, templates = {}, {}, [] + for source_dir in SOURCE_DIRS: + paths = sorted((ROOT / source_dir).rglob("*" + SOURCE_SUFFIX), key=lambda path: path.as_posix()) + for path in paths: + relative = path.relative_to(ROOT).as_posix() + text = path.read_text(encoding="utf-8", errors="replace") + for value in set(DECLARATION_CALL.findall(text)): + calls.setdefault(value, set()).add(relative) + for value in set(STRING_LITERAL.findall(text)): + parts = INTERPOLATION.split(value) + own_text = "".join(parts) + if len(parts) == 1: + literals.setdefault(value, set()).add(relative) + elif re.search(r"[A-Za-z]", own_text): + wildcard = ".+" if len(own_text) >= 3 else "[^A-Z]+" + pattern = re.compile(wildcard.join(re.escape(part) for part in parts)) + templates.append((pattern, len(own_text), relative)) + return calls, literals, templates + + +def declaring_files(identifier, calls, literals, templates): + for declarations in (calls, literals): + if identifier in declarations: + return sorted(declarations[identifier]) + best, files = 0, set() + for pattern, specificity, path in templates: + if specificity < best or not pattern.fullmatch(identifier): + continue + if specificity > best: + best, files = specificity, set() + files.add(path) + return sorted(files) + + +def screens_named(actions): + screens, seen = [], set() + for action in actions: + for name, kind in SCREEN_MENTION.findall(action): + words = name.replace('"', "").split() + if all(word.lower() in GENERIC_SCREEN_WORDS for word in words): + continue + screen = " ".join(words + [kind]) + if screen.lower() not in seen: + seen.add(screen.lower()) + screens.append(screen) + return screens + + +def build_index(): + calls, literals, templates = scan_sources() + journeys, errors = [], [] + paths = sorted((ROOT / JOURNEYS_DIR).rglob("*.xml"), key=lambda path: path.as_posix()) + for path in paths: + relative = path.relative_to(ROOT).as_posix() + try: + root = ET.parse(path).getroot() + except ET.ParseError as error: + errors.append(f"{relative}: {error}") + continue + actions = [" ".join("".join(action.itertext()).split()) for action in root.iter("action")] + identifiers = {} + for action in actions: + for identifier in IDENTIFIER_MENTION.findall(action): + if identifier in identifiers: + continue + identifiers[identifier] = declaring_files(identifier, calls, literals, templates) + if not identifiers[identifier]: + errors.append(f'{relative}: identifier "{identifier}" is not declared in any source file') + journeys.append( + { + "file": relative, + "name": root.get("name", ""), + "identifiers": identifiers, + "screens": screens_named(actions), + } + ) + return {"generatedBy": COMMAND, "journeys": journeys}, errors + + +def main(): + parser = argparse.ArgumentParser(description="Generate or check journeys/index.json.") + parser.add_argument("--check", action="store_true", help="fail when journeys/index.json does not match the tree") + args = parser.parse_args() + + index, errors = build_index() + generated = json.dumps(index, indent=2, ensure_ascii=False) + "\n" + index_path = ROOT / INDEX_FILE + if args.check: + committed = index_path.read_text(encoding="utf-8") if index_path.exists() else "" + if committed != generated: + diff = difflib.unified_diff( + committed.splitlines(keepends=True), generated.splitlines(keepends=True), INDEX_FILE, "generated", n=1 + ) + sys.stderr.writelines(itertools.islice(diff, 80)) + errors.insert(0, f"{INDEX_FILE} is stale; run `{COMMAND}` and commit the result") + else: + index_path.write_text(generated, encoding="utf-8") + + for error in errors: + print(error, file=sys.stderr) + return 1 if errors else 0 + + +if __name__ == "__main__": + sys.exit(main()) From 65fb1364cde2681a589aca422ddfafef337167b6 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 10 Sep 2026 20:46:30 +0200 Subject: [PATCH 2/3] docs: require journeys in pr qa notes --- .agents/commands/pr.md | 29 +++++++++++++++++++---------- .github/pull_request_template.md | 11 ++++++++++- AGENTS.md | 10 ++++++++++ 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/.agents/commands/pr.md b/.agents/commands/pr.md index 18f25b53fa..736e921ddd 100644 --- a/.agents/commands/pr.md +++ b/.agents/commands/pr.md @@ -38,6 +38,11 @@ If no base branch argument provided, detect the repo's default branch: - Use result as default (typically `main` or `master`) - If command fails, fall back to `master` +### 2.6. Regenerate the Journeys Index +- Run `python3 scripts/journeys_index.py` +- If `journeys/index.json` changed, commit it as `chore: update journeys index` +- If it reports an identifier that no source file declares, stop and report the journey and the identifier + ### 3. Gather Context - Get current branch name: `git branch --show-current` - Extract repo identifier: `git remote get-url origin | sed 's/\.git$//' | sed -E 's#.*[:/]([^/]+/[^/]+)$#\1#'` (e.g., `synonymdev/bitkit-android`) @@ -45,6 +50,8 @@ If no base branch argument provided, detect the repo's default branch: - Fetch 10 most recent PRs (open or closed) from the extracted repo for writing style reference - Run `git log $base..HEAD --oneline` for commit messages - Run `git diff $base...HEAD --stat` for understanding scope of changes +- List the journeys the branch adds or updates: `git diff --name-only --diff-filter=d $base...HEAD -- journeys | grep '\.xml$'` +- List the journeys whose route the branch may change: the `journeys/index.json` entries that name an identifier declared in a file from `git diff --name-only $base...HEAD` - Read `docs/screens-map.md` as the starting point for locating relevant Figma frames when the diff has user-visible UI changes - **If custom instructions provided:** - If instructions reference a specific commit SHA (pattern like `commit [a-f0-9]{7,40}`): @@ -133,9 +140,14 @@ When the user provides custom instructions after `--`: - Always use this structure: ```md ### QA Notes + #### Journeys #### Manual Tests #### Automated Checks ``` +- Under `#### Journeys`, list every journey the branch adds or updates (Step 3) as a list item with its repo path, e.g. `journeys/widgets/widgets-intro.xml`. +- Write `N/A — no user-visible behaviour change.` under `#### Journeys` only when the diff changes no user-visible behaviour. When it does and the branch adds or updates no journey, stop and report the flows that need one. +- Check each journey whose route the branch may change (Step 3) against the diff. If one no longer matches, stop and report it; the branch updates that journey first. +- Under `#### Manual Tests`, keep only what a journey cannot express, such as hardware, push notifications, or a companion app, and end each item with the reason. Write no manual test for a flow a listed journey covers. - Keep local verification commands, Gradle tasks, detekt, lint, unit tests, build passes, cargo test, cargo clippy, npm test, typecheck, CI coverage, or similar automated checks out of `#### Manual Tests`; summarize them under `#### Automated Checks` when they add useful context. - Use `#### Automated Checks` to summarize automated verification evidence, prioritizing coverage added, modified, or removed, each with the test file name and a short explanation. - Reference test files by bare file name only (e.g. `HwWalletRepoTest.kt`), never the full path. Only when two referenced test files share the same name, prefix the shortest leading path segment(s) that disambiguate them (e.g. `repositories/FooTest.kt` vs `viewmodels/FooTest.kt`). @@ -147,7 +159,7 @@ When the user provides custom instructions after `--`: - If no automated checks were run and no automated coverage changed, write `N/A` under `#### Automated Checks`. - Write manual tests using this template: ```md - - [ ] **{numbering}.** {optional_condition + →} {screen_action} → {next_screen_action}: expectation + - [ ] **{numbering}.** {optional_condition + →} {screen_action} → {next_screen_action}: expectation — {reason a journey cannot express it} ``` - Use a list of unchecked checkboxes for each individual test. - Use a numbered prefix for each test, in bold, for example `**1.**`, `**2.**`. @@ -184,16 +196,13 @@ Example: Concrete style target: ```md ### QA Notes +#### Journeys +- `journeys/amount-limits/send-amount-over-balance.xml` +- `journeys/widgets/add-widgets-flow.xml` #### Manual Tests -- [ ] **1.** No usable channels/spending balance → scan LN invoice: error shows immediately, not after 15s. -- [ ] **2.** Scanner → scan fixed amount LN invoice: Send Confirm or QuickPay opens directly. -- [ ] **3a.** `regression:` Send → scanner/paste fixed amount LN invoice: in-sheet nav to Confirm or QuickPay. - - [ ] **3b.** `regression:` Variable amount LN invoice/LNURL-pay: lands on Amount view. -- [ ] **4a.** Activity Detail of LN transfer → tap Connection: lands on Channel Detail. - - [ ] **4b.** back: returns to Activity Detail. -- [ ] **5a.** Settings → Lightning Connections → tap channel: still opens Channel Detail. - - [ ] **5b.** back: returns to Connections List. -- [ ] **6.** `regression:` Channel Detail → tap Close Connection: works. +- [ ] **1a.** Physical device → Scanner → scan a printed fixed amount LN invoice QR: Send Confirm opens directly — needs a device camera. + - [ ] **1b.** `regression:` Printed LNURL-pay QR: lands on Amount view — needs a device camera. +- [ ] **2.** Pubky Ring installed → Profile → sign in with Pubky Ring: profile shows as connected — needs the Pubky Ring app. #### Automated Checks - Unit tests added: cover invoice timeout handling in `SendInvoiceTest.kt`. - Unit tests modified: update channel navigation assertions in `ChannelDetailTest.kt`. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a8c3a59701..2503f1aedb 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -19,5 +19,14 @@ ### QA Notes - +#### Journeys + + + +#### Manual Tests + + + +#### Automated Checks + diff --git a/AGENTS.md b/AGENTS.md index 1a5913c006..2d4ce9f8a7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -262,6 +262,16 @@ suspend fun getData(): Result = withContext(Dispatchers.IO) { - ALWAYS use `.toImmutableList()`, `.toImmutableMap()`, `.toImmutableSet()` when producing collections for UI state - ALWAYS use `persistentListOf()`, `persistentMapOf()`, `persistentSetOf()` for default values in UiState fields +### Journeys + +`journeys/` holds XML walkthroughs of app behaviour that an agent evaluates on a running emulator. They are the QA contract for PRs. + +- ALWAYS add or update the journeys that prove a user-visible behaviour change in the same PR +- ALWAYS update a journey in the same PR when the PR changes the route it walks; `journeys/index.json` lists the source files that declare each identifier a journey names +- ALWAYS name an identifier in a journey action as `testTag "Name"` +- ALWAYS run `python3 scripts/journeys_index.py` after changing a journey or an identifier a journey names, and commit `journeys/index.json`; CI runs it with `--check` and fails on a stale index or on an identifier that no source file declares +- ALWAYS list every journey the PR adds or updates under `#### Journeys` in its QA Notes; `#### Manual Tests` keeps only what a journey cannot express + ### Changelog - NEVER edit `CHANGELOG.md` in normal feature/fix PRs; release automation collects changelog fragments into it From 872e09bc2d9fc3149983ed2be07ccd5cfd4c077a Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 10 Sep 2026 21:18:36 +0200 Subject: [PATCH 3/3] chore: simplify journeys index and rules --- .agents/commands/pr.md | 6 +- .github/pull_request_template.md | 4 +- .github/workflows/journeys.yml | 4 - AGENTS.md | 7 +- journeys/index.json | 1525 +++++++++++++----------------- scripts/journeys_index.py | 149 ++- 6 files changed, 733 insertions(+), 962 deletions(-) diff --git a/.agents/commands/pr.md b/.agents/commands/pr.md index 736e921ddd..d63c8a38a2 100644 --- a/.agents/commands/pr.md +++ b/.agents/commands/pr.md @@ -51,7 +51,6 @@ If no base branch argument provided, detect the repo's default branch: - Run `git log $base..HEAD --oneline` for commit messages - Run `git diff $base...HEAD --stat` for understanding scope of changes - List the journeys the branch adds or updates: `git diff --name-only --diff-filter=d $base...HEAD -- journeys | grep '\.xml$'` -- List the journeys whose route the branch may change: the `journeys/index.json` entries that name an identifier declared in a file from `git diff --name-only $base...HEAD` - Read `docs/screens-map.md` as the starting point for locating relevant Figma frames when the diff has user-visible UI changes - **If custom instructions provided:** - If instructions reference a specific commit SHA (pattern like `commit [a-f0-9]{7,40}`): @@ -144,9 +143,8 @@ When the user provides custom instructions after `--`: #### Manual Tests #### Automated Checks ``` -- Under `#### Journeys`, list every journey the branch adds or updates (Step 3) as a list item with its repo path, e.g. `journeys/widgets/widgets-intro.xml`. -- Write `N/A — no user-visible behaviour change.` under `#### Journeys` only when the diff changes no user-visible behaviour. When it does and the branch adds or updates no journey, stop and report the flows that need one. -- Check each journey whose route the branch may change (Step 3) against the diff. If one no longer matches, stop and report it; the branch updates that journey first. +- Under `#### Journeys`, list each journey the branch adds or updates (Step 3) as a list item with its repo path. Write `N/A — no user-visible behaviour change.` only when the diff changes no user-visible behaviour; when it does and the list is empty, stop and report the flows that need a journey. +- Stop and report any journey whose `sources` in `journeys/index.json` include a file the branch changes and whose route no longer matches the diff. - Under `#### Manual Tests`, keep only what a journey cannot express, such as hardware, push notifications, or a companion app, and end each item with the reason. Write no manual test for a flow a listed journey covers. - Keep local verification commands, Gradle tasks, detekt, lint, unit tests, build passes, cargo test, cargo clippy, npm test, typecheck, CI coverage, or similar automated checks out of `#### Manual Tests`; summarize them under `#### Automated Checks` when they add useful context. - Use `#### Automated Checks` to summarize automated verification evidence, prioritizing coverage added, modified, or removed, each with the test file name and a short explanation. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2503f1aedb..b148fd336b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -21,11 +21,11 @@ #### Journeys - + #### Manual Tests - + #### Automated Checks diff --git a/.github/workflows/journeys.yml b/.github/workflows/journeys.yml index fa9133bf2f..7a4992f2b2 100644 --- a/.github/workflows/journeys.yml +++ b/.github/workflows/journeys.yml @@ -8,10 +8,6 @@ on: permissions: contents: read -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - jobs: index: name: Check journeys index diff --git a/AGENTS.md b/AGENTS.md index 2d4ce9f8a7..c4092e311b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -266,11 +266,10 @@ suspend fun getData(): Result = withContext(Dispatchers.IO) { `journeys/` holds XML walkthroughs of app behaviour that an agent evaluates on a running emulator. They are the QA contract for PRs. -- ALWAYS add or update the journeys that prove a user-visible behaviour change in the same PR -- ALWAYS update a journey in the same PR when the PR changes the route it walks; `journeys/index.json` lists the source files that declare each identifier a journey names +- ALWAYS add or update, in the same PR, the journeys that prove a user-visible behaviour change and every journey whose route the PR changes; `journeys/index.json` lists the source files each journey's identifiers are declared in - ALWAYS name an identifier in a journey action as `testTag "Name"` -- ALWAYS run `python3 scripts/journeys_index.py` after changing a journey or an identifier a journey names, and commit `journeys/index.json`; CI runs it with `--check` and fails on a stale index or on an identifier that no source file declares -- ALWAYS list every journey the PR adds or updates under `#### Journeys` in its QA Notes; `#### Manual Tests` keeps only what a journey cannot express +- ALWAYS run `python3 scripts/journeys_index.py` after changing a journey or an identifier it names, and commit `journeys/index.json`; CI fails on a stale index or an undeclared identifier +- ALWAYS list the journeys a PR adds or updates under `#### Journeys` in its QA Notes ### Changelog diff --git a/journeys/index.json b/journeys/index.json index a0547383a9..5cc9290445 100644 --- a/journeys/index.json +++ b/journeys/index.json @@ -1,855 +1,670 @@ -{ - "generatedBy": "python3 scripts/journeys_index.py", - "journeys": [ - { - "file": "journeys/amount-limits/external-amount-over-max.xml", - "name": "external node amount over max is blocked", - "identifiers": { - "FundManual": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/FundingAdvancedScreen.kt", - "app/src/main/java/to/bitkit/ui/screens/transfer/FundingScreen.kt" - ], - "NodeIdInput": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt" - ], - "HostInput": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt", - "app/src/main/java/to/bitkit/ui/settings/advanced/ElectrumConfigScreen.kt" - ], - "PortInput": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt", - "app/src/main/java/to/bitkit/ui/settings/advanced/ElectrumConfigScreen.kt" - ], - "ExternalContinue": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt" - ], - "ExternalAmount": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalAmountScreen.kt" - ], - "N9": [ - "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" - ], - "ExternalAmountNumberField": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalAmountScreen.kt" - ], - "NRemove": [ - "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" - ] - }, - "screens": [ - "funding options screen", - "external node amount screen" - ] - }, - { - "file": "journeys/amount-limits/send-amount-over-balance.xml", - "name": "send amount over balance is blocked", - "identifiers": { - "Send": [ - "app/src/main/java/to/bitkit/ui/components/TabBar.kt" - ], - "RecipientManual": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendRecipientScreen.kt" - ], - "RecipientInput": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAddressScreen.kt" - ], - "AddressContinue": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAddressScreen.kt" - ], - "send_amount_screen": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" - ], - "available_balance": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" - ], - "N9": [ - "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" - ], - "SendNumberField": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" - ], - "NRemove": [ - "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" - ] - }, - "screens": [ - "Send amount screen" - ] - }, - { - "file": "journeys/amount-limits/transfer-spending-advanced-over-max.xml", - "name": "receiving capacity amount over max is blocked", - "identifiers": { - "SpendingAmount": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" - ], - "SpendingAmountAvailable": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" - ], - "SpendingAmountQuarter": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" - ], - "SpendingAmountContinue": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" - ], - "SpendingAdvanced": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAdvancedScreen.kt" - ], - "N9": [ - "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" - ], - "SpendingAdvancedNumberField": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAdvancedScreen.kt" - ], - "NRemove": [ - "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" - ] - }, - "screens": [ - "home screen", - "transfer intro screen", - "spending amount screen", - "confirm screen", - "receiving capacity screen" - ] - }, - { - "file": "journeys/amount-limits/transfer-spending-over-max.xml", - "name": "transfer to spending amount over max is blocked", - "identifiers": { - "SpendingAmount": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" - ], - "SpendingAmountAvailable": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" - ], - "N9": [ - "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" - ], - "SpendingAmountNumberField": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" - ], - "NRemove": [ - "app/src/main/java/to/bitkit/ui/components/NumberPad.kt" - ] - }, - "screens": [ - "home screen", - "transfer intro screen", - "spending amount screen" - ] - }, - { - "file": "journeys/cjit-notifications/cjit-foreground-service-notification.xml", - "name": "cjit notification via foreground service is single and formatted", - "identifiers": {}, - "screens": [ - "wallet home screen", - "received-transaction sheet" - ] - }, - { - "file": "journeys/cjit-notifications/cjit-push-single-notification.xml", - "name": "cjit push notification is delivered exactly once", - "identifiers": {}, - "screens": [] - }, - { - "file": "journeys/cjit-notifications/non-cjit-channel-no-payment-notification.xml", - "name": "non-cjit channel opening shows no payment notification", - "identifiers": { - "SpendingBalanceReadyToast": [ - "app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt" - ] - }, - "screens": [ - "wallet home screen" - ] - }, - { - "file": "journeys/deeplinks/screen-deeplink.xml", - "name": "screen deeplink", - "identifiers": {}, - "screens": [ - "Settings screen", - "log detail screen" - ] - }, - { - "file": "journeys/deeplinks/sheet-deeplink.xml", - "name": "sheet deeplink", - "identifiers": {}, - "screens": [ - "Send sheet" - ] - }, - { - "file": "journeys/hardware-wallet/activity-blue-icons.xml", - "name": "Hardware Wallet Activity Blue Icons", - "identifiers": {}, - "screens": [ - "wallet home screen", - "detail screen" - ] - }, - { - "file": "journeys/hardware-wallet/activity-detail-hw-tags.xml", - "name": "Hardware Wallet Activity Tags And Inputs Outputs", - "identifiers": {}, - "screens": [ - "wallet home screen", - "activity detail screen", - "Activity Explorer screen" - ] - }, - { - "file": "journeys/hardware-wallet/connect-flow.xml", - "name": "Hardware Wallet Connect Flow From Settings", - "identifiers": { - "HardwareWalletsScreen": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ], - "AddHardwareWallet": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ], - "HardwareWalletIntroScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" - ], - "HardwareWalletIntroContinue": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" - ], - "HardwareWalletSearchingScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwSearchingSheet.kt" - ], - "HardwareWalletFoundScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" - ], - "HardwareWalletFoundConnect": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" - ], - "HardwareWalletPairedScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ], - "HardwareWalletLabelInput": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ], - "HardwareWalletPairedFinish": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ] - }, - "screens": [ - "Hardware Wallets screen" - ] - }, - { - "file": "journeys/hardware-wallet/connect-home-tile.xml", - "name": "Hardware Wallet Connect and Home Tile", - "identifiers": { - "HardwareWalletScreen": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ] - }, - "screens": [ - "wallet home screen", - "hardware wallet detail screen" - ] - }, - { - "file": "journeys/hardware-wallet/detail-overview.xml", - "name": "Hardware Wallet Detail Overview", - "identifiers": { - "HardwareWalletScreen": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ], - "HardwareTransferToSpending": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ], - "HardwareTransferAmount": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "RemoveHardwareWallet": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ] - }, - "screens": [ - "wallet home screen", - "hardware wallet detail screen", - "transfer amount screen", - "activity detail screen" - ] - }, - { - "file": "journeys/hardware-wallet/passphrase-duplicate.xml", - "name": "Hardware Wallet Passphrase Duplicate", - "identifiers": { - "AddHardwareWallet": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ], - "HardwareWalletIntroContinue": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" - ], - "HardwareWalletFoundConnect": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" - ], - "HardwareWalletPairedPassphrase": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ], - "HardwareWalletPassphraseInput": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" - ], - "HardwareWalletPassphraseContinue": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" - ], - "HardwareWalletPassphraseScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" - ] - }, - "screens": [ - "wallet home screen" - ] - }, - { - "file": "journeys/hardware-wallet/passphrase-pairing.xml", - "name": "Hardware Wallet Passphrase Pairing", - "identifiers": { - "AddHardwareWallet": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ], - "HardwareWalletIntroContinue": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" - ], - "HardwareWalletFoundScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" - ], - "HardwareWalletFoundConnect": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" - ], - "HardwareWalletPairedScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ], - "HardwareWalletPairedPassphrase": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ], - "HardwareWalletPairedFinish": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ], - "HardwareWalletPassphraseScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" - ], - "HardwareWalletPassphraseContinue": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" - ], - "HardwareWalletPassphraseInput": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" - ], - "HardwareWalletPassphrasePairedScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphrasePairedSheet.kt" - ], - "HardwareWalletLabelInput": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ], - "HardwareWalletScreen": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ] - }, - "screens": [ - "wallet home screen", - "hardware wallet detail screen" - ] - }, - { - "file": "journeys/hardware-wallet/passphrase-settings-remove.xml", - "name": "Hardware Wallet Passphrase Settings And Remove", - "identifiers": { - "HardwareWalletsScreen": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ], - "RenameHardwareWalletInput": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ], - "RenameHardwareWalletSave": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ] - }, - "screens": [ - "rename sheet", - "Hardware Wallets screen", - "wallet home screen", - "detail screen" - ] - }, - { - "file": "journeys/hardware-wallet/passphrase-transfer-to-spending.xml", - "name": "Hardware Wallet Passphrase Transfer To Spending", - "identifiers": { - "HardwareWalletScreen": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ], - "HardwareTransferToSpending": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ], - "HardwareTransferAmountQuarter": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "HardwareTransferAmountContinue": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "HardwareTransferSign": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" - ], - "HardwareTransferOpenTrezorConnect": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" - ], - "HardwareTransferSigned": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignedScreen.kt" - ], - "HwTransferPassphraseSheet": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt" - ], - "HwTransferPassphraseInput": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt" - ], - "HwTransferPassphraseContinue": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt" - ] - }, - "screens": [ - "wallet home screen", - "detail screen", - "sign screen", - "transaction signed screen", - "Processing Payment screen", - "home screen", - "passphrase sheet" - ] - }, - { - "file": "journeys/hardware-wallet/receive-onchain.xml", - "name": "Hardware Wallet On-chain Receive", - "identifiers": { - "Receive": [ - "app/src/main/java/to/bitkit/ui/components/TabBar.kt" - ], - "ReceiveScreen": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveSheet.kt" - ], - "QRCode": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt" - ], - "ShowDetails": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt" - ], - "ReceiveHardwareAddress": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt" - ], - "HardwareVerifyAddress": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt" - ] - }, - "screens": [ - "wallet home screen", - "Receive sheet" - ] - }, - { - "file": "journeys/hardware-wallet/send-onchain.xml", - "name": "Hardware Wallet On-chain Send", - "identifiers": { - "Send": [ - "app/src/main/java/to/bitkit/ui/components/TabBar.kt" - ], - "RecipientManual": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendRecipientScreen.kt" - ], - "RecipientInput": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAddressScreen.kt" - ], - "AddressContinue": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAddressScreen.kt" - ], - "send_amount_screen": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" - ], - "AssetButton-switch": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" - ], - "ContinueAmount": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveAmountScreen.kt", - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt" - ], - "SendConfirmAssetButton": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" - ], - "HardwareSendAmount": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/HwSendSignScreen.kt" - ], - "HardwareSendAddress": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/HwSendSignScreen.kt" - ], - "HardwareSendOpenTrezorConnect": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/HwSendSignScreen.kt" - ], - "SendSuccess": [ - "app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt" - ] - }, - "screens": [ - "wallet home screen", - "Send amount screen", - "Send review screen", - "hardware sign screen", - "success screen" - ] - }, - { - "file": "journeys/hardware-wallet/settings-hardware-wallets.xml", - "name": "Hardware Wallet Settings", - "identifiers": { - "HardwareWalletsScreen": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ], - "RenameHardwareWalletInput": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ], - "RenameHardwareWalletSave": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ], - "AddHardwareWallet": [ - "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" - ], - "HardwareWalletIntroScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" - ] - }, - "screens": [ - "Hardware Wallets screen", - "Rename Hardware Wallet bottom sheet" - ] - }, - { - "file": "journeys/hardware-wallet/suggestion-intro-sheet.xml", - "name": "Hardware Wallet Suggestion Card and Intro Sheet", - "identifiers": { - "HardwareWalletIntroScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" - ], - "HardwareWalletIntroContinue": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" - ], - "HardwareWalletSearchingScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwSearchingSheet.kt" - ], - "HardwareWalletSearchingCancel": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwSearchingSheet.kt" - ], - "HardwareWalletFoundScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" - ], - "HardwareWalletFoundConnect": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt" - ], - "HardwareWalletPairedScreen": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ], - "HardwareWalletLabelInput": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ], - "HardwareWalletPairedFinish": [ - "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt" - ] - }, - "screens": [ - "wallet home screen", - "home screen" - ] - }, - { - "file": "journeys/hardware-wallet/transfer-to-spending-max-lsp-cap.xml", - "name": "Hardware Wallet Transfer To Spending MAX LSP Cap", - "identifiers": { - "HardwareWalletScreen": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ], - "HardwareTransferToSpending": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ], - "HardwareTransferAmount": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "HardwareTransferAmountMax": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "HardwareTransferAmountContinue": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "HardwareTransferSign": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" - ], - "HardwareTransferOpenTrezorConnect": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" - ], - "HardwareTransferSigned": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignedScreen.kt" - ] - }, - "screens": [ - "wallet home screen", - "hardware wallet detail screen", - "transfer amount screen", - "sign screen", - "transaction signed screen" - ] - }, - { - "file": "journeys/hardware-wallet/transfer-to-spending-node-warmup.xml", - "name": "Hardware Wallet Transfer To Spending Node Warmup", - "identifiers": { - "HardwareTransferToSpending": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ], - "HardwareTransferAmountQuarter": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "HardwareTransferAmountContinue": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "HardwareTransferSign": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" - ] - }, - "screens": [ - "wallet home screen", - "transfer amount screen", - "amount screen", - "sign screen" - ] - }, - { - "file": "journeys/hardware-wallet/transfer-to-spending.xml", - "name": "Hardware Wallet Transfer To Spending", - "identifiers": { - "HardwareWalletScreen": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ], - "HardwareTransferToSpending": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" - ], - "HardwareTransferAmount": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "HardwareTransferAmountQuarter": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "HardwareTransferAmountContinue": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt" - ], - "HardwareTransferSign": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" - ], - "HardwareTransferOpenTrezorConnect": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt" - ], - "HardwareTransferSigned": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignedScreen.kt" - ] - }, - "screens": [ - "wallet home screen", - "hardware wallet detail screen", - "transfer amount screen", - "sign screen", - "transaction signed screen", - "detail screen" - ] - }, - { - "file": "journeys/hardware-wallet/usb-reconnect.xml", - "name": "Hardware Wallet USB Reconnect", - "identifiers": {}, - "screens": [ - "wallet home screen" - ] - }, - { - "file": "journeys/notification-permission/receive-cjit-confirm-notification-toggle.xml", - "name": "receive cjit confirm toggle requests notification permission", - "identifiers": { - "ReceiveConfirmNotificationSwitch": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt" - ] - }, - "screens": [ - "home screen", - "Receive sheet", - "amount screen", - "confirm screen" - ] - }, - { - "file": "journeys/notification-permission/receive-cjit-liquidity-notification-toggle.xml", - "name": "receive cjit liquidity toggle requests notification permission", - "identifiers": { - "ReceiveLiquidityNotificationSwitch": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveLiquidityScreen.kt" - ] - }, - "screens": [ - "home screen", - "Receive sheet", - "amount screen", - "confirm screen", - "liquidity screen" - ] - }, - { - "file": "journeys/notification-permission/toggle-off-opens-system-settings.xml", - "name": "notification toggle off opens system settings", - "identifiers": { - "ReceiveConfirmNotificationSwitch": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt" - ] - }, - "screens": [ - "home screen", - "Receive sheet", - "amount screen", - "confirm screen", - "Android system notification settings screen" - ] - }, - { - "file": "journeys/notification-permission/transfer-spending-confirm-notification-toggle.xml", - "name": "transfer spending confirm toggle requests notification permission", - "identifiers": { - "SpendingAmount": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" - ], - "SpendingAmountAvailable": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" - ], - "SpendingAmountContinue": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" - ], - "SpendingConfirmNotificationSwitch": [ - "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingConfirmScreen.kt" - ] - }, - "screens": [ - "home screen", - "transfer intro screen", - "spending amount screen", - "spending confirm screen" - ] - }, - { - "file": "journeys/payment-requests/issuer-interoperability.xml", - "name": "Paykit Issuer Interoperability", - "identifiers": { - "PaymentRequestConfirm": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" - ], - "PaymentRequestsBell": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt" - ], - "PaymentRequestsSheet": [ - "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" - ], - "PaymentRequestRow-71300000-0000-4000-8000-000000000001": [ - "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" - ], - "PaymentRequestPay-71300000-0000-4000-8000-000000000001": [ - "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" - ], - "SendConfirmToggleDetails": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" - ], - "ReviewContactRecipient": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" - ] - }, - "screens": [ - "Payment Request confirmation screen", - "incoming Payment Requests sheet" - ] - }, - { - "file": "journeys/pubky-marketplace/wallet-leg.xml", - "name": "pubky marketplace wallet leg", - "identifiers": { - "PubkyAuthWatchOnlyConsent": [ - "app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt" - ], - "PubkyAuthWatchOnlyApprove": [ - "app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt" - ], - "PubkyAuthAuthorize": [ - "app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt" - ], - "PubkyAuthOK": [ - "app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt" - ], - "ContactPaymentsToggle": [ - "app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt" - ], - "Contact_": [ - "app/src/main/java/to/bitkit/ui/screens/contacts/ContactsScreen.kt" - ], - "Contact_": [ - "app/src/main/java/to/bitkit/ui/screens/contacts/ContactsScreen.kt" - ], - "ReviewAmount": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" - ], - "PaymentRequestsBell": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt" - ], - "PaymentRequestsSheet": [ - "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" - ], - "PaymentRequestRow-": [ - "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" - ], - "PaymentRequestPay-": [ - "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt" - ], - "ReviewContactRecipient": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" - ], - "GRAB": [ - "app/src/main/java/to/bitkit/ui/components/SwipeToConfirm.kt" - ], - "SendSuccess": [ - "app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt" - ], - "ActivityAmount": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt" - ], - "ActivityTxDetails": [ - "app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt" - ] - }, - "screens": [ - "watch-only consent screen", - "authorization screen", - "incoming requests sheet" - ] - }, - { - "file": "journeys/widgets/add-widgets-flow.xml", - "name": "add widgets flow", - "identifiers": {}, - "screens": [ - "Add Widget bottom sheet" - ] - }, - { - "file": "journeys/widgets/widgets-intro.xml", - "name": "widgets intro", - "identifiers": {}, - "screens": [ - "Add Widget bottom sheet" - ] - } - ] -} +[ + { + "file": "journeys/amount-limits/external-amount-over-max.xml", + "name": "external node amount over max is blocked", + "identifiers": [ + "FundManual", + "NodeIdInput", + "HostInput", + "PortInput", + "ExternalContinue", + "ExternalAmount", + "N9", + "ExternalAmountNumberField", + "NRemove" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/FundingAdvancedScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/FundingScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalAmountScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConnectionScreen.kt", + "app/src/main/java/to/bitkit/ui/settings/advanced/ElectrumConfigScreen.kt" + ], + "screens": [ + "funding options screen", + "external node amount screen" + ] + }, + { + "file": "journeys/amount-limits/send-amount-over-balance.xml", + "name": "send amount over balance is blocked", + "identifiers": [ + "Send", + "RecipientManual", + "RecipientInput", + "AddressContinue", + "send_amount_screen", + "available_balance", + "N9", + "SendNumberField", + "NRemove" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt", + "app/src/main/java/to/bitkit/ui/components/TabBar.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAddressScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendRecipientScreen.kt" + ], + "screens": [ + "Send amount screen" + ] + }, + { + "file": "journeys/amount-limits/transfer-spending-advanced-over-max.xml", + "name": "receiving capacity amount over max is blocked", + "identifiers": [ + "SpendingAmount", + "SpendingAmountAvailable", + "SpendingAmountQuarter", + "SpendingAmountContinue", + "SpendingAdvanced", + "N9", + "SpendingAdvancedNumberField", + "NRemove" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAdvancedScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "screens": [ + "home screen", + "transfer intro screen", + "spending amount screen", + "confirm screen", + "receiving capacity screen" + ] + }, + { + "file": "journeys/amount-limits/transfer-spending-over-max.xml", + "name": "transfer to spending amount over max is blocked", + "identifiers": [ + "SpendingAmount", + "SpendingAmountAvailable", + "N9", + "SpendingAmountNumberField", + "NRemove" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/components/NumberPad.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt" + ], + "screens": [ + "home screen", + "transfer intro screen", + "spending amount screen" + ] + }, + { + "file": "journeys/cjit-notifications/cjit-foreground-service-notification.xml", + "name": "cjit notification via foreground service is single and formatted", + "identifiers": [], + "sources": [], + "screens": [ + "wallet home screen", + "received-transaction sheet" + ] + }, + { + "file": "journeys/cjit-notifications/cjit-push-single-notification.xml", + "name": "cjit push notification is delivered exactly once", + "identifiers": [], + "sources": [], + "screens": [] + }, + { + "file": "journeys/cjit-notifications/non-cjit-channel-no-payment-notification.xml", + "name": "non-cjit channel opening shows no payment notification", + "identifiers": [ + "SpendingBalanceReadyToast" + ], + "sources": [ + "app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt" + ], + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/deeplinks/screen-deeplink.xml", + "name": "screen deeplink", + "identifiers": [], + "sources": [], + "screens": [ + "Settings screen", + "log detail screen" + ] + }, + { + "file": "journeys/deeplinks/sheet-deeplink.xml", + "name": "sheet deeplink", + "identifiers": [], + "sources": [], + "screens": [ + "Send sheet" + ] + }, + { + "file": "journeys/hardware-wallet/activity-blue-icons.xml", + "name": "Hardware Wallet Activity Blue Icons", + "identifiers": [], + "sources": [], + "screens": [ + "wallet home screen", + "detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/activity-detail-hw-tags.xml", + "name": "Hardware Wallet Activity Tags And Inputs Outputs", + "identifiers": [], + "sources": [], + "screens": [ + "wallet home screen", + "activity detail screen", + "Activity Explorer screen" + ] + }, + { + "file": "journeys/hardware-wallet/connect-flow.xml", + "name": "Hardware Wallet Connect Flow From Settings", + "identifiers": [ + "HardwareWalletsScreen", + "AddHardwareWallet", + "HardwareWalletIntroScreen", + "HardwareWalletIntroContinue", + "HardwareWalletSearchingScreen", + "HardwareWalletFoundScreen", + "HardwareWalletFoundConnect", + "HardwareWalletPairedScreen", + "HardwareWalletLabelInput", + "HardwareWalletPairedFinish" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwSearchingSheet.kt" + ], + "screens": [ + "Hardware Wallets screen" + ] + }, + { + "file": "journeys/hardware-wallet/connect-home-tile.xml", + "name": "Hardware Wallet Connect and Home Tile", + "identifiers": [ + "HardwareWalletScreen" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "screens": [ + "wallet home screen", + "hardware wallet detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/detail-overview.xml", + "name": "Hardware Wallet Detail Overview", + "identifiers": [ + "HardwareWalletScreen", + "HardwareTransferToSpending", + "HardwareTransferAmount", + "RemoveHardwareWallet" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "screens": [ + "wallet home screen", + "hardware wallet detail screen", + "transfer amount screen", + "activity detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-duplicate.xml", + "name": "Hardware Wallet Passphrase Duplicate", + "identifiers": [ + "AddHardwareWallet", + "HardwareWalletIntroContinue", + "HardwareWalletFoundConnect", + "HardwareWalletPairedPassphrase", + "HardwareWalletPassphraseInput", + "HardwareWalletPassphraseContinue", + "HardwareWalletPassphraseScreen" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" + ], + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-pairing.xml", + "name": "Hardware Wallet Passphrase Pairing", + "identifiers": [ + "AddHardwareWallet", + "HardwareWalletIntroContinue", + "HardwareWalletFoundScreen", + "HardwareWalletFoundConnect", + "HardwareWalletPairedScreen", + "HardwareWalletPairedPassphrase", + "HardwareWalletPairedFinish", + "HardwareWalletPassphraseScreen", + "HardwareWalletPassphraseContinue", + "HardwareWalletPassphraseInput", + "HardwareWalletPassphrasePairedScreen", + "HardwareWalletLabelInput", + "HardwareWalletScreen" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt", + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphrasePairedSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPassphraseSheet.kt" + ], + "screens": [ + "wallet home screen", + "hardware wallet detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-settings-remove.xml", + "name": "Hardware Wallet Passphrase Settings And Remove", + "identifiers": [ + "HardwareWalletsScreen", + "RenameHardwareWalletInput", + "RenameHardwareWalletSave" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt" + ], + "screens": [ + "rename sheet", + "Hardware Wallets screen", + "wallet home screen", + "detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-transfer-to-spending.xml", + "name": "Hardware Wallet Passphrase Transfer To Spending", + "identifiers": [ + "HardwareWalletScreen", + "HardwareTransferToSpending", + "HardwareTransferAmountQuarter", + "HardwareTransferAmountContinue", + "HardwareTransferSign", + "HardwareTransferOpenTrezorConnect", + "HardwareTransferSigned", + "HwTransferPassphraseSheet", + "HwTransferPassphraseInput", + "HwTransferPassphraseContinue" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignedScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "screens": [ + "wallet home screen", + "detail screen", + "sign screen", + "transaction signed screen", + "Processing Payment screen", + "home screen", + "passphrase sheet" + ] + }, + { + "file": "journeys/hardware-wallet/receive-onchain.xml", + "name": "Hardware Wallet On-chain Receive", + "identifiers": [ + "Receive", + "ReceiveScreen", + "QRCode", + "ShowDetails", + "ReceiveHardwareAddress", + "HardwareVerifyAddress" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/components/TabBar.kt", + "app/src/main/java/to/bitkit/ui/screens/settings/LegacyRnRecoveryScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveQrScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveSheet.kt" + ], + "screens": [ + "wallet home screen", + "Receive sheet" + ] + }, + { + "file": "journeys/hardware-wallet/send-onchain.xml", + "name": "Hardware Wallet On-chain Send", + "identifiers": [ + "Send", + "RecipientManual", + "RecipientInput", + "AddressContinue", + "send_amount_screen", + "AssetButton-switch", + "ContinueAmount", + "SendConfirmAssetButton", + "HardwareSendAmount", + "HardwareSendAddress", + "HardwareSendOpenTrezorConnect", + "SendSuccess" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/components/TabBar.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveAmountScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/HwSendSignScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAddressScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendRecipientScreen.kt", + "app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt" + ], + "screens": [ + "wallet home screen", + "Send amount screen", + "Send review screen", + "hardware sign screen", + "success screen" + ] + }, + { + "file": "journeys/hardware-wallet/settings-hardware-wallets.xml", + "name": "Hardware Wallet Settings", + "identifiers": [ + "HardwareWalletsScreen", + "RenameHardwareWalletInput", + "RenameHardwareWalletSave", + "AddHardwareWallet", + "HardwareWalletIntroScreen" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/settings/general/HardwareWalletsSettingsScreen.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt" + ], + "screens": [ + "Hardware Wallets screen", + "Rename Hardware Wallet bottom sheet" + ] + }, + { + "file": "journeys/hardware-wallet/suggestion-intro-sheet.xml", + "name": "Hardware Wallet Suggestion Card and Intro Sheet", + "identifiers": [ + "HardwareWalletIntroScreen", + "HardwareWalletIntroContinue", + "HardwareWalletSearchingScreen", + "HardwareWalletSearchingCancel", + "HardwareWalletFoundScreen", + "HardwareWalletFoundConnect", + "HardwareWalletPairedScreen", + "HardwareWalletLabelInput", + "HardwareWalletPairedFinish" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwFoundSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwIntroSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwPairedSheet.kt", + "app/src/main/java/to/bitkit/ui/sheets/hardware/HwSearchingSheet.kt" + ], + "screens": [ + "wallet home screen", + "home screen" + ] + }, + { + "file": "journeys/hardware-wallet/transfer-to-spending-max-lsp-cap.xml", + "name": "Hardware Wallet Transfer To Spending MAX LSP Cap", + "identifiers": [ + "HardwareWalletScreen", + "HardwareTransferToSpending", + "HardwareTransferAmount", + "HardwareTransferAmountMax", + "HardwareTransferAmountContinue", + "HardwareTransferSign", + "HardwareTransferOpenTrezorConnect", + "HardwareTransferSigned" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignedScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "screens": [ + "wallet home screen", + "hardware wallet detail screen", + "transfer amount screen", + "sign screen", + "transaction signed screen" + ] + }, + { + "file": "journeys/hardware-wallet/transfer-to-spending-node-warmup.xml", + "name": "Hardware Wallet Transfer To Spending Node Warmup", + "identifiers": [ + "HardwareTransferToSpending", + "HardwareTransferAmountQuarter", + "HardwareTransferAmountContinue", + "HardwareTransferSign" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "screens": [ + "wallet home screen", + "transfer amount screen", + "amount screen", + "sign screen" + ] + }, + { + "file": "journeys/hardware-wallet/transfer-to-spending.xml", + "name": "Hardware Wallet Transfer To Spending", + "identifiers": [ + "HardwareWalletScreen", + "HardwareTransferToSpending", + "HardwareTransferAmount", + "HardwareTransferAmountQuarter", + "HardwareTransferAmountContinue", + "HardwareTransferSign", + "HardwareTransferOpenTrezorConnect", + "HardwareTransferSigned" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingAmountHwScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/hardware/SpendingHwSignedScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/HardwareWalletScreen.kt" + ], + "screens": [ + "wallet home screen", + "hardware wallet detail screen", + "transfer amount screen", + "sign screen", + "transaction signed screen", + "detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/usb-reconnect.xml", + "name": "Hardware Wallet USB Reconnect", + "identifiers": [], + "sources": [], + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/notification-permission/receive-cjit-confirm-notification-toggle.xml", + "name": "receive cjit confirm toggle requests notification permission", + "identifiers": [ + "ReceiveConfirmNotificationSwitch" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt" + ], + "screens": [ + "home screen", + "Receive sheet", + "amount screen", + "confirm screen" + ] + }, + { + "file": "journeys/notification-permission/receive-cjit-liquidity-notification-toggle.xml", + "name": "receive cjit liquidity toggle requests notification permission", + "identifiers": [ + "ReceiveLiquidityNotificationSwitch" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveLiquidityScreen.kt" + ], + "screens": [ + "home screen", + "Receive sheet", + "amount screen", + "confirm screen", + "liquidity screen" + ] + }, + { + "file": "journeys/notification-permission/toggle-off-opens-system-settings.xml", + "name": "notification toggle off opens system settings", + "identifiers": [ + "ReceiveConfirmNotificationSwitch" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt" + ], + "screens": [ + "home screen", + "Receive sheet", + "amount screen", + "confirm screen", + "Android system notification settings screen" + ] + }, + { + "file": "journeys/notification-permission/transfer-spending-confirm-notification-toggle.xml", + "name": "transfer spending confirm toggle requests notification permission", + "identifiers": [ + "SpendingAmount", + "SpendingAmountAvailable", + "SpendingAmountContinue", + "SpendingConfirmNotificationSwitch" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingAmountScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/transfer/SpendingConfirmScreen.kt" + ], + "screens": [ + "home screen", + "transfer intro screen", + "spending amount screen", + "spending confirm screen" + ] + }, + { + "file": "journeys/payment-requests/issuer-interoperability.xml", + "name": "Paykit Issuer Interoperability", + "identifiers": [ + "PaymentRequestConfirm", + "PaymentRequestsBell", + "PaymentRequestsSheet", + "PaymentRequestRow-71300000-0000-4000-8000-000000000001", + "PaymentRequestPay-71300000-0000-4000-8000-000000000001", + "SendConfirmToggleDetails", + "ReviewContactRecipient" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt" + ], + "screens": [ + "Payment Request confirmation screen", + "incoming Payment Requests sheet" + ] + }, + { + "file": "journeys/pubky-marketplace/wallet-leg.xml", + "name": "pubky marketplace wallet leg", + "identifiers": [ + "PubkyAuthWatchOnlyConsent", + "PubkyAuthWatchOnlyApprove", + "PubkyAuthAuthorize", + "PubkyAuthOK", + "ContactPaymentsToggle", + "Contact_", + "Contact_", + "ReviewAmount", + "PaymentRequestsBell", + "PaymentRequestsSheet", + "PaymentRequestRow-", + "PaymentRequestPay-", + "ReviewContactRecipient", + "GRAB", + "SendSuccess", + "ActivityAmount", + "ActivityTxDetails" + ], + "sources": [ + "app/src/main/java/to/bitkit/ui/components/SwipeToConfirm.kt", + "app/src/main/java/to/bitkit/ui/screens/contacts/ContactsScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/paymentrequests/PaymentRequestsScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/profile/PubkyAuthApprovalSheet.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt", + "app/src/main/java/to/bitkit/ui/screens/wallets/send/SendConfirmScreen.kt", + "app/src/main/java/to/bitkit/ui/settings/SettingsScreen.kt", + "app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt" + ], + "screens": [ + "watch-only consent screen", + "authorization screen", + "incoming requests sheet" + ] + }, + { + "file": "journeys/widgets/add-widgets-flow.xml", + "name": "add widgets flow", + "identifiers": [], + "sources": [], + "screens": [ + "Add Widget bottom sheet" + ] + }, + { + "file": "journeys/widgets/widgets-intro.xml", + "name": "widgets intro", + "identifiers": [], + "sources": [], + "screens": [ + "Add Widget bottom sheet" + ] + } +] diff --git a/scripts/journeys_index.py b/scripts/journeys_index.py index e6d2146977..d94d5112f3 100755 --- a/scripts/journeys_index.py +++ b/scripts/journeys_index.py @@ -1,24 +1,20 @@ #!/usr/bin/env python3 -"""Generate journeys/index.json: what each journey's actions name. +r"""Generate journeys/index.json: the identifiers, source files and screens each journey names. -Per journeys/**/*.xml file the index records the file, the , each identifier its -actions name with the source files that declare it, and the screens its actions name. +For each journeys/**/*.xml file the index records the file, the , the identifiers its +actions name, the source files that declare those identifiers, and the screens its actions name. -An identifier resolves to the files with a testTag("…") call for it; failing that, to the files -with a string literal equal to it (a named argument, a constant, a conditional branch); failing -that, to the files with the most specific string template that can produce it ("N$text"). In a -template with fewer than three characters of its own, an interpolation matches no capital letters, -so "N$text" produces N9 but not NavigationBack. +A source file declares an identifier when it contains it as a string literal, or as a string template +whose text before the first interpolation begins the identifier while the rest of the identifier has +no capital letters: "N$text" and "N\(number)" declare N9 but not NRemove. python3 scripts/journeys_index.py write journeys/index.json - python3 scripts/journeys_index.py --check fail when journeys/index.json does not match the tree + python3 scripts/journeys_index.py --check fail when journeys/index.json is stale Both modes fail when a journey names an identifier that no source file declares. """ import argparse -import difflib -import itertools import json import re import sys @@ -26,77 +22,46 @@ from pathlib import Path ROOT = Path(__file__).resolve().parent.parent -JOURNEYS_DIR = "journeys" -INDEX_FILE = "journeys/index.json" -COMMAND = "python3 scripts/journeys_index.py" +INDEX = ROOT / "journeys" / "index.json" # Platform: an action names an identifier as testTag "Name"; Kotlin sources declare it. IDENTIFIER_MENTION = re.compile(r'\btestTag "([^"]+)"') SOURCE_DIRS = ("app/src/main", "app/src/debug", "app/src/release") SOURCE_SUFFIX = ".kt" -STRING_LITERAL = re.compile(r'"((?:[^"\\\n$]|\\.|\$\{[^{}\n]*\}|\$)*)"') -INTERPOLATION = re.compile(r"\$\{[^{}\n]*\}|\$[A-Za-z_]\w*") -DECLARATION_CALL = re.compile(r"\btestTag\(\s*" + STRING_LITERAL.pattern + r"\s*\)") -SCREEN_WORD = r'(?:"[^"]+"|(?!(?:a|an|and|at|for|from|in|into|its|of|on|or|the|to|with)\b)[\w-]+)' -SCREEN_MENTION = re.compile(rf"\b(?:[Tt]he|[Aa]n?|[Ii]ts)\s+({SCREEN_WORD}(?:\s+{SCREEN_WORD}){{0,3}}?)\s+(screen|sheet)\b") -GENERIC_SCREEN_WORDS = {"bottom", "current", "entire", "first", "last", "new", "next", "other", "previous", "same", "whole"} +# A string literal "Name", or a string template "Name$…" or "Name\(…)" that begins with Name. +STRING_START = re.compile(r'"([\w-]+)("|\$|\\\()') +SCREEN_WORD = r'(?:"[^"]+"|(?!(?:a|an|bottom|its|same|the)\b)[\w-]+)' +SCREEN_MENTION = re.compile( + rf"\b(?:[Tt]he|[Aa]n?|[Ii]ts)\s+({SCREEN_WORD}(?:\s+{SCREEN_WORD}){{0,3}}?)\s+(screen|(?:bottom\s+)?sheet)\b" +) def scan_sources(): - calls, literals, templates = {}, {}, [] + literals, prefixes = {}, {} for source_dir in SOURCE_DIRS: - paths = sorted((ROOT / source_dir).rglob("*" + SOURCE_SUFFIX), key=lambda path: path.as_posix()) - for path in paths: + for path in (ROOT / source_dir).rglob("*" + SOURCE_SUFFIX): relative = path.relative_to(ROOT).as_posix() - text = path.read_text(encoding="utf-8", errors="replace") - for value in set(DECLARATION_CALL.findall(text)): - calls.setdefault(value, set()).add(relative) - for value in set(STRING_LITERAL.findall(text)): - parts = INTERPOLATION.split(value) - own_text = "".join(parts) - if len(parts) == 1: - literals.setdefault(value, set()).add(relative) - elif re.search(r"[A-Za-z]", own_text): - wildcard = ".+" if len(own_text) >= 3 else "[^A-Z]+" - pattern = re.compile(wildcard.join(re.escape(part) for part in parts)) - templates.append((pattern, len(own_text), relative)) - return calls, literals, templates - - -def declaring_files(identifier, calls, literals, templates): - for declarations in (calls, literals): - if identifier in declarations: - return sorted(declarations[identifier]) - best, files = 0, set() - for pattern, specificity, path in templates: - if specificity < best or not pattern.fullmatch(identifier): - continue - if specificity > best: - best, files = specificity, set() - files.add(path) - return sorted(files) - - -def screens_named(actions): - screens, seen = [], set() - for action in actions: - for name, kind in SCREEN_MENTION.findall(action): - words = name.replace('"', "").split() - if all(word.lower() in GENERIC_SCREEN_WORDS for word in words): - continue - screen = " ".join(words + [kind]) - if screen.lower() not in seen: - seen.add(screen.lower()) - screens.append(screen) - return screens + for text, end in STRING_START.findall(path.read_text(encoding="utf-8", errors="replace")): + declarations = literals if end == '"' else prefixes + declarations.setdefault(text, set()).add(relative) + return literals, prefixes + + +def declaring_files(identifier, literals, prefixes): + if identifier in literals: + return literals[identifier] + for end in range(len(identifier) - 1, 0, -1): + prefix, rest = identifier[:end], identifier[end:] + if prefix in prefixes: + return set() if re.search("[A-Z]", rest) else prefixes[prefix] + return set() def build_index(): - calls, literals, templates = scan_sources() + literals, prefixes = scan_sources() journeys, errors = [], [] - paths = sorted((ROOT / JOURNEYS_DIR).rglob("*.xml"), key=lambda path: path.as_posix()) - for path in paths: + for path in sorted((ROOT / "journeys").rglob("*.xml")): relative = path.relative_to(ROOT).as_posix() try: root = ET.parse(path).getroot() @@ -104,43 +69,41 @@ def build_index(): errors.append(f"{relative}: {error}") continue actions = [" ".join("".join(action.itertext()).split()) for action in root.iter("action")] - identifiers = {} - for action in actions: - for identifier in IDENTIFIER_MENTION.findall(action): - if identifier in identifiers: - continue - identifiers[identifier] = declaring_files(identifier, calls, literals, templates) - if not identifiers[identifier]: - errors.append(f'{relative}: identifier "{identifier}" is not declared in any source file') + identifiers = list(dict.fromkeys(name for action in actions for name in IDENTIFIER_MENTION.findall(action))) + sources = set() + for identifier in identifiers: + files = declaring_files(identifier, literals, prefixes) + if not files: + errors.append(f'{relative}: identifier "{identifier}" is not declared in any source file') + sources |= files + screens = [ + " ".join(name.replace('"', "").split() + [kind]) + for action in actions + for name, kind in SCREEN_MENTION.findall(action) + ] journeys.append( { "file": relative, "name": root.get("name", ""), "identifiers": identifiers, - "screens": screens_named(actions), + "sources": sorted(sources), + "screens": list(dict.fromkeys(screens)), } ) - return {"generatedBy": COMMAND, "journeys": journeys}, errors + return journeys, errors def main(): parser = argparse.ArgumentParser(description="Generate or check journeys/index.json.") - parser.add_argument("--check", action="store_true", help="fail when journeys/index.json does not match the tree") - args = parser.parse_args() - - index, errors = build_index() - generated = json.dumps(index, indent=2, ensure_ascii=False) + "\n" - index_path = ROOT / INDEX_FILE - if args.check: - committed = index_path.read_text(encoding="utf-8") if index_path.exists() else "" - if committed != generated: - diff = difflib.unified_diff( - committed.splitlines(keepends=True), generated.splitlines(keepends=True), INDEX_FILE, "generated", n=1 - ) - sys.stderr.writelines(itertools.islice(diff, 80)) - errors.insert(0, f"{INDEX_FILE} is stale; run `{COMMAND}` and commit the result") - else: - index_path.write_text(generated, encoding="utf-8") + parser.add_argument("--check", action="store_true", help="fail when journeys/index.json is stale") + check = parser.parse_args().check + + journeys, errors = build_index() + generated = json.dumps(journeys, indent=2, ensure_ascii=False) + "\n" + if not check: + INDEX.write_text(generated, encoding="utf-8") + elif not INDEX.exists() or INDEX.read_text(encoding="utf-8") != generated: + errors.insert(0, "journeys/index.json is stale; run `python3 scripts/journeys_index.py` and commit it") for error in errors: print(error, file=sys.stderr)