From 9aa09f255df82c8ba5a856a99be88c36171037e8 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 10 Sep 2026 20:45:40 +0200 Subject: [PATCH 1/4] chore: add journeys index check --- .github/workflows/journeys.yml | 26 + journeys/index.json | 1029 ++++++++++++++++++++++++++++++++ scripts/journeys_index.py | 151 +++++ 3 files changed, 1206 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 000000000..fa9133bf2 --- /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 000000000..d6bb33351 --- /dev/null +++ b/journeys/index.json @@ -0,0 +1,1029 @@ +{ + "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": [ + "Bitkit/Views/Transfer/FundAdvancedOptions.swift", + "Bitkit/Views/Transfer/FundingOptions.swift" + ], + "NodeIdInput": [ + "Bitkit/Views/Transfer/FundManualSetupView.swift" + ], + "HostInput": [ + "Bitkit/Views/Settings/Advanced/ElectrumSettingsScreen.swift", + "Bitkit/Views/Transfer/FundManualSetupView.swift" + ], + "PortInput": [ + "Bitkit/Views/Settings/Advanced/ElectrumSettingsScreen.swift", + "Bitkit/Views/Transfer/FundManualSetupView.swift" + ], + "ExternalContinue": [ + "Bitkit/Views/Transfer/FundManualSetupView.swift" + ], + "ExternalAmount": [ + "Bitkit/Views/Transfer/FundManualAmountView.swift" + ], + "ExternalAmountAvailable": [ + "Bitkit/Views/Transfer/FundManualAmountView.swift" + ], + "N9": [ + "Bitkit/Components/NumberPad.swift" + ], + "ExternalAmountExceededToast": [ + "Bitkit/Views/Transfer/FundManualAmountView.swift" + ], + "ExternalAmountNumberField": [ + "Bitkit/Views/Transfer/FundManualAmountView.swift" + ], + "NRemove": [ + "Bitkit/Components/NumberPad.swift" + ] + }, + "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": [ + "Bitkit/Components/TabBar/TabBar.swift" + ], + "RecipientManual": [ + "Bitkit/Views/Wallets/Send/SendOptionsView.swift" + ], + "RecipientInput": [ + "Bitkit/Views/Wallets/Send/SendEnterManuallyView.swift" + ], + "AddressContinue": [ + "Bitkit/Views/Wallets/Send/SendEnterManuallyView.swift" + ], + "SendAmount": [ + "Bitkit/Views/Wallets/Send/SendAmountView.swift" + ], + "AvailableAmount": [ + "Bitkit/Views/Wallets/Send/SendAmountView.swift" + ], + "N9": [ + "Bitkit/Components/NumberPad.swift" + ], + "SendAmountExceededToast": [ + "Bitkit/Views/Wallets/LnurlWithdraw/LnurlWithdrawAmount.swift", + "Bitkit/Views/Wallets/Send/LnurlPayAmount.swift", + "Bitkit/Views/Wallets/Send/SendAmountView.swift" + ], + "SendNumberField": [ + "Bitkit/Views/Wallets/LnurlWithdraw/LnurlWithdrawAmount.swift", + "Bitkit/Views/Wallets/Send/LnurlPayAmount.swift", + "Bitkit/Views/Wallets/Send/SendAmountView.swift" + ], + "NRemove": [ + "Bitkit/Components/NumberPad.swift" + ] + }, + "screens": [ + "Send amount screen" + ] + }, + { + "file": "journeys/amount-limits/transfer-spending-advanced-over-max.xml", + "name": "receiving capacity amount over max is blocked", + "identifiers": { + "ActivitySavings": [ + "Bitkit/Views/Home/HomeWalletView.swift" + ], + "TransferToSpending": [ + "Bitkit/Views/Wallets/SavingsWalletScreen.swift" + ], + "SpendingAmount": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "SpendingAmountAvailable": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "SpendingAmountMax": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "SpendingAmountContinue": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "SpendingConfirmAdvanced": [ + "Bitkit/Views/Transfer/SpendingConfirm.swift" + ], + "SpendingAdvanced": [ + "Bitkit/Views/Transfer/SpendingAdvancedView.swift" + ], + "SpendingAdvancedMax": [ + "Bitkit/Views/Transfer/SpendingAdvancedView.swift" + ], + "N9": [ + "Bitkit/Components/NumberPad.swift" + ], + "SpendingAdvancedNumberField": [ + "Bitkit/Views/Transfer/SpendingAdvancedView.swift" + ], + "SpendingAdvancedExceededToast": [ + "Bitkit/Views/Transfer/SpendingAdvancedView.swift" + ], + "NRemove": [ + "Bitkit/Components/NumberPad.swift" + ] + }, + "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": { + "ActivitySavings": [ + "Bitkit/Views/Home/HomeWalletView.swift" + ], + "TransferToSpending": [ + "Bitkit/Views/Wallets/SavingsWalletScreen.swift" + ], + "SpendingAmount": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "SpendingAmountAvailable": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "N9": [ + "Bitkit/Components/NumberPad.swift" + ], + "SpendingAmountExceededToast": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "SpendingAmountNumberField": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "NRemove": [ + "Bitkit/Components/NumberPad.swift" + ] + }, + "screens": [ + "home screen", + "transfer intro screen", + "spending amount screen" + ] + }, + { + "file": "journeys/cjit-notifications/cjit-background-notification.xml", + "name": "cjit notification while backgrounded is single and correctly typed", + "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": [ + "Bitkit/ViewModels/AppViewModel.swift" + ] + }, + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/hardware-wallet/activity-blue-icons.xml", + "name": "Hardware Wallet Activity Blue Icons", + "identifiers": { + "ActivityShowAll": [ + "Bitkit/Views/Wallets/Activity/ActivityLatest.swift" + ], + "Tab-sent": [ + "Bitkit/Components/SegmentedControl.swift" + ], + "Tab-received": [ + "Bitkit/Components/SegmentedControl.swift" + ], + "Tab-other": [ + "Bitkit/Components/SegmentedControl.swift" + ], + "Tab-all": [ + "Bitkit/Components/SegmentedControl.swift" + ] + }, + "screens": [ + "wallet home screen", + "detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/activity-detail-hw-tags.xml", + "name": "Hardware Wallet Activity Tags And Inputs Outputs", + "identifiers": { + "ActivityShowAll": [ + "Bitkit/Views/Wallets/Activity/ActivityLatest.swift" + ], + "ActivityTag": [ + "Bitkit/Views/Wallets/Activity/ActivityItemView.swift" + ], + "TagInput": [ + "Bitkit/Views/Wallets/Sheets/AddTagSheet.swift" + ], + "ActivityTagsSubmit": [ + "Bitkit/Views/Wallets/Sheets/AddTagSheet.swift" + ], + "ActivityTags": [ + "Bitkit/Views/Wallets/Activity/ActivityItemView.swift" + ], + "ActivityTxDetails": [ + "Bitkit/Views/Wallets/Activity/ActivityItemView.swift" + ], + "TagsPrompt": [ + "Bitkit/Components/Activity/ActivityListFilter.swift" + ] + }, + "screens": [ + "wallet home screen", + "activity detail screen", + "detail screen", + "Activity Explorer screen" + ] + }, + { + "file": "journeys/hardware-wallet/connect-flow.xml", + "name": "Hardware Wallet Connect Flow From Settings", + "identifiers": { + "TrezorForgetDevice-bridge": [ + "Bitkit/Components/Trezor/TrezorDeviceRow.swift" + ], + "Tab-general": [ + "Bitkit/Components/SegmentedControl.swift" + ], + "HardwareWalletsSettings": [ + "Bitkit/Views/Settings/GeneralSettingsView.swift" + ], + "HardwareWalletsScreen": [ + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" + ], + "AddHardwareWallet": [ + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" + ], + "HardwareWalletIntroScreen": [ + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "HardwareWalletIntroContinue": [ + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "HardwareWalletSearchingScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwSearchingView.swift" + ], + "HardwareWalletFoundScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" + ], + "HardwareWalletFoundConnect": [ + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" + ], + "HardwareWalletPairedScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ], + "HardwareWalletLabelInput": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ], + "HardwareWalletPairedFinish": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ] + }, + "screens": [ + "Hardware Wallets screen" + ] + }, + { + "file": "journeys/hardware-wallet/connect-home-tile.xml", + "name": "Hardware Wallet Connect and Home Tile", + "identifiers": { + "DrawerSettings": [ + "Bitkit/Components/DrawerView.swift" + ], + "TrezorScanButton": [ + "Bitkit/Views/Trezor/TrezorDeviceListView.swift" + ], + "TrezorDevice-bridge": [ + "Bitkit/Components/Trezor/TrezorDeviceRow.swift" + ], + "ActivityHardware": [ + "Bitkit/Components/HardwareWalletsGrid.swift" + ], + "HardwareWalletScreen": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ] + }, + "screens": [ + "wallet home screen", + "hardware wallet detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/detail-overview.xml", + "name": "Hardware Wallet Detail Overview", + "identifiers": { + "ActivityHardware": [ + "Bitkit/Components/HardwareWalletsGrid.swift" + ], + "HardwareWalletScreen": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferToSpending": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferAmount": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "RemoveHardwareWallet": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "RemoveHwWalletDialog": [ + "Bitkit/Components/RemoveHwWalletDialog.swift" + ] + }, + "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": { + "ActivityHardware": [ + "Bitkit/Components/HardwareWalletsGrid.swift" + ], + "Tab-general": [ + "Bitkit/Components/SegmentedControl.swift" + ], + "HardwareWalletsSettings": [ + "Bitkit/Views/Settings/GeneralSettingsView.swift" + ], + "AddHardwareWallet": [ + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" + ], + "HardwareWalletIntroContinue": [ + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "HardwareWalletFoundConnect": [ + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" + ], + "HardwareWalletPairedPassphrase": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ], + "HardwareWalletPassphraseInput": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" + ], + "HardwareWalletPassphraseContinue": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" + ], + "HwPassphraseError": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" + ], + "HardwareWalletPassphraseScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" + ] + }, + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-pairing.xml", + "name": "Hardware Wallet Passphrase Pairing", + "identifiers": { + "Tab-general": [ + "Bitkit/Components/SegmentedControl.swift" + ], + "HardwareWalletsSettings": [ + "Bitkit/Views/Settings/GeneralSettingsView.swift" + ], + "AddHardwareWallet": [ + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" + ], + "HardwareWalletIntroContinue": [ + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "HardwareWalletFoundScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" + ], + "HardwareWalletFoundConnect": [ + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" + ], + "HardwareWalletPairedScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ], + "HardwareWalletPairedPassphrase": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ], + "HardwareWalletPairedFinish": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ], + "HardwareWalletPassphraseScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" + ], + "HardwareWalletPassphraseContinue": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" + ], + "HardwareWalletPassphraseInput": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" + ], + "HardwareWalletPassphrasePairedScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ], + "HardwareWalletLabelInput": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ], + "HardwareWalletScreen": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ] + }, + "screens": [ + "wallet home screen", + "hardware wallet detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-settings-remove.xml", + "name": "Hardware Wallet Passphrase Settings And Remove", + "identifiers": { + "DrawerSettings": [ + "Bitkit/Components/DrawerView.swift" + ], + "Tab-general": [ + "Bitkit/Components/SegmentedControl.swift" + ], + "HardwareWalletsSettings": [ + "Bitkit/Views/Settings/GeneralSettingsView.swift" + ], + "HardwareWalletsScreen": [ + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" + ], + "RenameHardwareWalletInput": [ + "Bitkit/Views/Sheets/RenameHardwareWalletSheet.swift" + ], + "RenameHardwareWalletSave": [ + "Bitkit/Views/Sheets/RenameHardwareWalletSheet.swift" + ], + "RemoveHwWalletDialog": [ + "Bitkit/Components/RemoveHwWalletDialog.swift" + ] + }, + "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": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferToSpending": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferAmountQuarter": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferAmountContinue": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferSign": [ + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" + ], + "HardwareTransferOpenTrezorConnect": [ + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" + ], + "HardwareTransferSigned": [ + "Bitkit/Views/Transfer/Hardware/SpendingHwSigned.swift" + ], + "HwTransferPassphraseSheet": [ + "Bitkit/Views/Transfer/Hardware/HwPassphrasePromptSheet.swift" + ], + "HwTransferPassphraseInput": [ + "Bitkit/Views/Transfer/Hardware/HwPassphrasePromptSheet.swift" + ], + "HwTransferPassphraseContinue": [ + "Bitkit/Views/Transfer/Hardware/HwPassphrasePromptSheet.swift" + ] + }, + "screens": [ + "wallet home screen", + "detail screen", + "sign screen", + "transaction signed screen", + "Processing Payment screen", + "home screen", + "passphrase sheet" + ] + }, + { + "file": "journeys/hardware-wallet/reconnect.xml", + "name": "Hardware Wallet Reconnect", + "identifiers": { + "TrezorDisconnectButton": [ + "Bitkit/Views/Trezor/TrezorConnectedView.swift" + ], + "ActivityHardware": [ + "Bitkit/Components/HardwareWalletsGrid.swift" + ], + "TrezorKnownDeviceConnect-bridge": [ + "Bitkit/Components/Trezor/TrezorDeviceRow.swift" + ] + }, + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/hardware-wallet/settings-hardware-wallets.xml", + "name": "Hardware Wallet Settings", + "identifiers": { + "DrawerSettings": [ + "Bitkit/Components/DrawerView.swift" + ], + "Tab-general": [ + "Bitkit/Components/SegmentedControl.swift" + ], + "HardwareWalletsSettings": [ + "Bitkit/Views/Settings/GeneralSettingsView.swift" + ], + "HardwareWalletsScreen": [ + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" + ], + "RenameHardwareWalletInput": [ + "Bitkit/Views/Sheets/RenameHardwareWalletSheet.swift" + ], + "RenameHardwareWalletSave": [ + "Bitkit/Views/Sheets/RenameHardwareWalletSheet.swift" + ], + "AddHardwareWallet": [ + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" + ], + "HardwareWalletIntroScreen": [ + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "HardwareWalletIntroCancel": [ + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "RemoveHwWalletDialog": [ + "Bitkit/Components/RemoveHwWalletDialog.swift" + ], + "TrezorScanButton": [ + "Bitkit/Views/Trezor/TrezorDeviceListView.swift" + ] + }, + "screens": [ + "Hardware Wallets screen", + "Rename Hardware Wallet sheet" + ] + }, + { + "file": "journeys/hardware-wallet/suggestion-intro-sheet.xml", + "name": "Hardware Wallet Suggestion Card and Intro Sheet", + "identifiers": { + "TrezorForgetDevice-bridge": [ + "Bitkit/Components/Trezor/TrezorDeviceRow.swift" + ], + "ActivityHardware": [ + "Bitkit/Components/HardwareWalletsGrid.swift" + ], + "HardwareWalletIntroScreen": [ + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "HardwareWalletIntroContinue": [ + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "HardwareWalletSearchingScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwSearchingView.swift" + ], + "HardwareWalletSearchingCancel": [ + "Bitkit/Views/Sheets/HardwareConnect/HwSearchingView.swift" + ], + "HardwareWalletFoundScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" + ], + "HardwareWalletFoundConnect": [ + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" + ], + "HardwareWalletPairedScreen": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ], + "HardwareWalletLabelInput": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ], + "HardwareWalletPairedFinish": [ + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" + ] + }, + "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": { + "ActivityHardware": [ + "Bitkit/Components/HardwareWalletsGrid.swift" + ], + "HardwareWalletScreen": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferToSpending": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferAmount": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferAmountAvailable": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferAmountMax": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferAmountContinue": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferSign": [ + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" + ], + "HardwareTransferOpenTrezorConnect": [ + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" + ], + "HardwareTransferSigned": [ + "Bitkit/Views/Transfer/Hardware/SpendingHwSigned.swift" + ] + }, + "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": { + "ActivityHardware": [ + "Bitkit/Components/HardwareWalletsGrid.swift" + ], + "HardwareTransferToSpending": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferAmountQuarter": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferAmountContinue": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferSign": [ + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" + ] + }, + "screens": [ + "wallet home screen", + "transfer amount screen", + "amount screen", + "sign screen" + ] + }, + { + "file": "journeys/hardware-wallet/transfer-to-spending-over-max.xml", + "name": "hardware transfer to spending amount over max is blocked", + "identifiers": { + "ActivityHardware": [ + "Bitkit/Components/HardwareWalletsGrid.swift" + ], + "HardwareWalletScreen": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferToSpending": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferAmount": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "N9": [ + "Bitkit/Components/NumberPad.swift" + ], + "HardwareTransferAmountAvailable": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferAmountExceededToast": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "NRemove": [ + "Bitkit/Components/NumberPad.swift" + ], + "NavigationBack": [ + "Bitkit/Components/NavigationBar.swift", + "Bitkit/Views/Security/AuthCheck.swift", + "Bitkit/Views/Sheets/Sheet.swift" + ] + }, + "screens": [ + "hardware wallet detail screen", + "transfer amount screen", + "hardware wallet screen" + ] + }, + { + "file": "journeys/hardware-wallet/transfer-to-spending.xml", + "name": "Hardware Wallet Transfer To Spending", + "identifiers": { + "ActivityHardware": [ + "Bitkit/Components/HardwareWalletsGrid.swift" + ], + "HardwareWalletScreen": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferToSpending": [ + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "HardwareTransferAmount": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferAmountAvailable": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferAmountQuarter": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferAmountContinue": [ + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" + ], + "HardwareTransferSign": [ + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" + ], + "HardwareTransferOpenTrezorConnect": [ + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" + ], + "HardwareTransferSigned": [ + "Bitkit/Views/Transfer/Hardware/SpendingHwSigned.swift" + ] + }, + "screens": [ + "wallet home screen", + "hardware wallet detail screen", + "transfer amount screen", + "sign screen", + "transaction signed screen", + "detail screen" + ] + }, + { + "file": "journeys/notification-permission/receive-cjit-confirm-notification-toggle.xml", + "name": "receive cjit confirm toggle requests notification permission", + "identifiers": { + "Receive": [ + "Bitkit/Components/TabBar/TabBar.swift" + ], + "ReceiveCjitAmount": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" + ], + "ReceiveCjitAmountContinue": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" + ], + "ReceiveCjitConfirm": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift" + ], + "ReceiveConfirmNotificationSwitch": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift" + ] + }, + "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": { + "Receive": [ + "Bitkit/Components/TabBar/TabBar.swift" + ], + "ReceiveCjitAmount": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" + ], + "ReceiveCjitAmountContinue": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" + ], + "ReceiveCjitLiquidity": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitLearnMore.swift" + ], + "ReceiveLiquidityNotificationSwitch": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitLearnMore.swift" + ] + }, + "screens": [ + "home screen", + "Receive sheet", + "amount screen", + "confirm screen", + "liquidity screen" + ] + }, + { + "file": "journeys/notification-permission/toggle-off-and-system-settings-route.xml", + "name": "notification toggle off unregisters and settings routes to system", + "identifiers": { + "Receive": [ + "Bitkit/Components/TabBar/TabBar.swift" + ], + "ReceiveCjitAmount": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" + ], + "ReceiveCjitAmountContinue": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" + ], + "ReceiveCjitConfirm": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift" + ], + "ReceiveConfirmNotificationSwitch": [ + "Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift" + ], + "DrawerSettings": [ + "Bitkit/Components/DrawerView.swift" + ], + "NotificationsOpenSystemSettings": [ + "Bitkit/Views/Settings/Notifications/NotificationsSettings.swift" + ] + }, + "screens": [ + "home screen", + "Receive sheet", + "amount screen", + "confirm screen", + "wallet home screen", + "Notifications settings screen" + ] + }, + { + "file": "journeys/notification-permission/transfer-spending-confirm-notification-toggle.xml", + "name": "transfer spending confirm toggle requests notification permission", + "identifiers": { + "ActivitySavings": [ + "Bitkit/Views/Home/HomeWalletView.swift" + ], + "TransferToSpending": [ + "Bitkit/Views/Wallets/SavingsWalletScreen.swift" + ], + "SpendingAmount": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "SpendingAmountAvailable": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "SpendingAmountContinue": [ + "Bitkit/Views/Transfer/SpendingAmount.swift" + ], + "SpendingConfirmNotificationSwitch": [ + "Bitkit/Views/Transfer/SpendingConfirm.swift" + ] + }, + "screens": [ + "home screen", + "transfer intro screen", + "spending amount screen", + "spending confirm screen" + ] + }, + { + "file": "journeys/pubky-marketplace/wallet-leg.xml", + "name": "pubky marketplace wallet leg", + "identifiers": { + "PubkyAuthWatchOnlyConsent": [ + "Bitkit/Views/Sheets/PubkyAuthApproval/PubkyAuthApprovalSheet.swift" + ], + "PubkyAuthWatchOnlyApprove": [ + "Bitkit/Views/Sheets/PubkyAuthApproval/PubkyAuthApprovalSheet.swift" + ], + "PubkyAuthAuthorize": [ + "Bitkit/Views/Sheets/PubkyAuthApproval/PubkyAuthApprovalSheet.swift" + ], + "PubkyAuthOK": [ + "Bitkit/Views/Sheets/PubkyAuthApproval/PubkyAuthApprovalSheet.swift" + ], + "ContactPaymentsToggle": [ + "Bitkit/Views/Settings/GeneralSettingsView.swift" + ], + "Contact_": [ + "Bitkit/Views/Contacts/ContactsListView.swift" + ], + "Contact_": [ + "Bitkit/Views/Contacts/ContactsListView.swift" + ], + "ReviewAmount": [ + "Bitkit/Views/Wallets/Send/LnurlPayConfirm.swift", + "Bitkit/Views/Wallets/Send/SendConfirmationView.swift" + ], + "PaymentRequestsBell": [ + "Bitkit/Components/Header.swift" + ], + "PaymentRequestsSheet": [ + "Bitkit/Views/PaymentRequests/PaymentRequestsView.swift" + ], + "PaymentRequestRow----one-time": [ + "Bitkit/Views/PaymentRequests/PaymentRequestsView.swift" + ], + "PaymentRequestPay-": [], + "ReviewContactRecipient": [ + "Bitkit/Views/Wallets/Send/SendConfirmationView.swift" + ], + "GRAB": [ + "Bitkit/Components/SwipeButton.swift" + ], + "SendSuccess": [ + "Bitkit/Views/Wallets/Send/SendSuccess.swift" + ], + "ActivityAmount": [ + "Bitkit/Views/Wallets/Activity/ActivityItemView.swift" + ], + "ActivityTxDetails": [ + "Bitkit/Views/Wallets/Activity/ActivityItemView.swift" + ] + }, + "screens": [ + "watch-only consent screen", + "authorization screen", + "incoming requests sheet" + ] + }, + { + "file": "journeys/widgets/add-widgets-flow.xml", + "name": "add widgets flow", + "identifiers": { + "DrawerWidgets": [ + "Bitkit/Components/DrawerView.swift" + ], + "WidgetsAdd": [ + "Bitkit/Views/Home/HomeWidgetsView.swift" + ], + "WidgetListItem-calculator": [ + "Bitkit/Views/Widgets/WidgetsListSheetView.swift" + ], + "WidgetSave": [ + "Bitkit/Views/Widgets/WidgetPreviewSheetView.swift" + ] + }, + "screens": [ + "Add Widget sheet" + ] + }, + { + "file": "journeys/widgets/widgets-intro.xml", + "name": "widgets intro", + "identifiers": { + "DrawerWidgets": [ + "Bitkit/Components/DrawerView.swift" + ], + "WidgetsOnboarding": [ + "Bitkit/Views/Widgets/WidgetsIntroView.swift" + ], + "WidgetsOnboardingAddWidget": [ + "Bitkit/Views/Widgets/WidgetsIntroView.swift" + ], + "WidgetListItem-weather": [ + "Bitkit/Views/Widgets/WidgetsListSheetView.swift" + ], + "WidgetSave": [ + "Bitkit/Views/Widgets/WidgetPreviewSheetView.swift" + ] + }, + "screens": [ + "widgets intro screen", + "Add Widget sheet" + ] + } + ] +} diff --git a/scripts/journeys_index.py b/scripts/journeys_index.py new file mode 100755 index 000000000..5caa5e7dd --- /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 an accessibilityIdentifier("…") call for it; failing that, +to the files with a string literal equal to it (a labelled argument, a constant, a conditional +branch); failing that, to the files with the most specific string template that can produce it +("N\\(number)"). In a template with fewer than three characters of its own, an interpolation +matches no capital letters, so "N\\(number)" 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 id "Name"; Swift sources declare it. +IDENTIFIER_MENTION = re.compile(r'\bid "([^"]+)"') +SOURCE_DIRS = ("Bitkit", "BitkitNotification", "BitkitWidget") +SOURCE_SUFFIX = ".swift" +STRING_LITERAL = re.compile(r'"((?:[^"\\\n]|\\\((?:[^()"\n]|"[^"\n]*"|\([^()\n]*\))*\)|\\.)*)"') +INTERPOLATION = re.compile(r'\\\((?:[^()"\n]|"[^"\n]*"|\([^()\n]*\))*\)') +DECLARATION_CALL = re.compile(r"\baccessibilityIdentifier\(\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 1d1a2fd56a31401e6ee0d96995a82088c99155cd Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 10 Sep 2026 20:45:41 +0200 Subject: [PATCH 2/4] docs: require journeys in pr qa notes --- .agents/commands/pr.md | 29 +++++++++++++++++++---------- .github/pull_request_template.md | 14 ++++++++++++++ AGENTS.md | 18 ++++++++++++------ journeys/README.md | 8 ++++---- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/.agents/commands/pr.md b/.agents/commands/pr.md index 05f1cc9bf..d16c55318 100644 --- a/.agents/commands/pr.md +++ b/.agents/commands/pr.md @@ -35,6 +35,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-ios`) @@ -42,6 +47,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` ### 4. Extract Linked Issues Scan commits for issue references: @@ -127,9 +134,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, `xcodebuild`, Swift tests, SwiftFormat, translation validation, 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 with file paths and a short explanation. - For removed automated coverage, state why it was removed. @@ -140,7 +152,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.**`. @@ -168,16 +180,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 `BitkitTests/TransferViewModelTests.swift`. - Unit tests modified: update channel navigation assertions in `BitkitTests/ChannelDetailsViewModelTests.swift`. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 526002e03..392a7e70b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -21,3 +21,17 @@ Add any links to GitHub issues or Asana tasks that are relevant to this pull req ### Screenshot / Video Insert relevant screenshot / recording + +### QA Notes + +#### Journeys + + + +#### Manual Tests + + + +#### Automated Checks + + diff --git a/AGENTS.md b/AGENTS.md index 5398d5923..2c783a92b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -156,9 +156,9 @@ xcodebuildmcp simulator test Separately from the test suites, `journeys/` holds XML walkthroughs of app behaviour that an agent evaluates by driving a running simulator — number pad caps, notification permission, widget flows, -hardware wallet pairing and transfers. They are developer assistance rather than a test layer: -nothing runs them in CI and they gate nothing. Read `journeys/README.md` before running or writing -one, and see the Journeys section under Code Style & Conventions. +hardware wallet pairing and transfers. They are the QA contract for PRs rather than a test suite: +CI does not run them, and it checks that `journeys/index.json` matches them. Read `journeys/README.md` +before running or writing one, and see the Journeys section under Code Style & Conventions. ## Architecture @@ -377,9 +377,15 @@ Ensure accessibility modifiers and labels are added to custom components. `` and the suite README — never assert Android behaviour iOS does not have. - SKIP a journey only when the iOS feature does not exist, and record it under "Not ported" in `journeys/README.md` with what is missing. -- Journeys are developer-assistance specs, not a QA gate. Nothing runs them in CI and no runner is - wired up for them; `ai-device-tests.yml` runs `TrezorBridgeDashboardUITests` and never reads - `journeys/`. An agent runs one on request. +- ADD or UPDATE the journeys that prove a user-visible behaviour change in the same PR. Journeys are + the QA contract for PRs: list every journey a PR adds or updates under `#### Journeys` in its QA + Notes, and keep `#### Manual Tests` to what a journey cannot express. +- 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. +- NAME an identifier in a journey action as `id "Name"`. +- 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. CI does not run the journeys; an agent runs one on request. - A journey that disagrees with the app is most likely stale rather than evidence of a bug. Say what you found and update the journey; escalate only once you have separately confirmed the app is wrong. diff --git a/journeys/README.md b/journeys/README.md index 61b295aaf..c49b53f73 100644 --- a/journeys/README.md +++ b/journeys/README.md @@ -9,10 +9,10 @@ and deliberately keep the same file names, journey names and `` prose so stay diffable. Only the platform mechanics differ — `adb` becomes `xcodebuildmcp`, and Android `testTag`s become iOS `accessibilityIdentifier`s (the vocabulary is shared; see [Identifiers](#identifiers)). -**Journeys are not a QA gate.** They are agent-evaluated and non-deterministic, nothing runs them in -CI, and there is no runner wired up for them yet — `ai-device-tests.yml` runs `TrezorBridgeDashboardUITests` -and does not read `journeys/`. Treat a journey as a well-written description of a flow, not as an -authority on what the app owes you. +**Journeys are the QA contract for PRs.** A PR that changes user-visible behaviour adds or updates the +journeys that prove it and lists them in its QA Notes; see the Journeys section in `AGENTS.md`. They are +agent-evaluated and non-deterministic, so CI does not run them. CI checks that `journeys/index.json` is +current and that every identifier a journey names is declared in source. A journey that no longer matches the app is most likely **stale**, not evidence of a bug. The corpus is new on iOS and has not been run end to end, so when the two disagree the first assumption should be From 7bfbe23f629676925acce8d326e5780c324813ba Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 10 Sep 2026 21:18:40 +0200 Subject: [PATCH 3/4] chore: simplify journeys index and rules --- .agents/commands/pr.md | 6 +- .github/pull_request_template.md | 4 +- .github/workflows/journeys.yml | 4 - AGENTS.md | 14 +- journeys/README.md | 6 +- journeys/index.json | 1793 +++++++++++++----------------- scripts/journeys_index.py | 149 +-- 7 files changed, 832 insertions(+), 1144 deletions(-) diff --git a/.agents/commands/pr.md b/.agents/commands/pr.md index d16c55318..11df5dfaf 100644 --- a/.agents/commands/pr.md +++ b/.agents/commands/pr.md @@ -48,7 +48,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` ### 4. Extract Linked Issues Scan commits for issue references: @@ -138,9 +137,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, `xcodebuild`, Swift tests, SwiftFormat, translation validation, 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 with file paths and a short explanation. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 392a7e70b..f15c14002 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -26,11 +26,11 @@ Insert relevant screenshot / recording #### Journeys - + #### Manual Tests - + #### Automated Checks diff --git a/.github/workflows/journeys.yml b/.github/workflows/journeys.yml index fa9133bf2..7a4992f2b 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 2c783a92b..c096d8e6b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -377,15 +377,13 @@ Ensure accessibility modifiers and labels are added to custom components. `` and the suite README — never assert Android behaviour iOS does not have. - SKIP a journey only when the iOS feature does not exist, and record it under "Not ported" in `journeys/README.md` with what is missing. -- ADD or UPDATE the journeys that prove a user-visible behaviour change in the same PR. Journeys are - the QA contract for PRs: list every journey a PR adds or updates under `#### Journeys` in its QA - Notes, and keep `#### Manual Tests` to what a journey cannot express. -- 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. +- 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. List those journeys under `#### Journeys` in the PR's QA Notes. - NAME an identifier in a journey action as `id "Name"`. -- 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. CI does not run the journeys; an agent runs one on request. +- 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; it does not run + the journeys themselves. - A journey that disagrees with the app is most likely stale rather than evidence of a bug. Say what you found and update the journey; escalate only once you have separately confirmed the app is wrong. diff --git a/journeys/README.md b/journeys/README.md index c49b53f73..fa13d4ef5 100644 --- a/journeys/README.md +++ b/journeys/README.md @@ -9,10 +9,8 @@ and deliberately keep the same file names, journey names and `` prose so stay diffable. Only the platform mechanics differ — `adb` becomes `xcodebuildmcp`, and Android `testTag`s become iOS `accessibilityIdentifier`s (the vocabulary is shared; see [Identifiers](#identifiers)). -**Journeys are the QA contract for PRs.** A PR that changes user-visible behaviour adds or updates the -journeys that prove it and lists them in its QA Notes; see the Journeys section in `AGENTS.md`. They are -agent-evaluated and non-deterministic, so CI does not run them. CI checks that `journeys/index.json` is -current and that every identifier a journey names is declared in source. +**Journeys are the QA contract for PRs** (see the Journeys section in `AGENTS.md`). They are +agent-evaluated and non-deterministic, so CI does not run them; it only checks `journeys/index.json`. A journey that no longer matches the app is most likely **stale**, not evidence of a bug. The corpus is new on iOS and has not been run end to end, so when the two disagree the first assumption should be diff --git a/journeys/index.json b/journeys/index.json index d6bb33351..7fafe5197 100644 --- a/journeys/index.json +++ b/journeys/index.json @@ -1,1029 +1,764 @@ -{ - "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": [ - "Bitkit/Views/Transfer/FundAdvancedOptions.swift", - "Bitkit/Views/Transfer/FundingOptions.swift" - ], - "NodeIdInput": [ - "Bitkit/Views/Transfer/FundManualSetupView.swift" - ], - "HostInput": [ - "Bitkit/Views/Settings/Advanced/ElectrumSettingsScreen.swift", - "Bitkit/Views/Transfer/FundManualSetupView.swift" - ], - "PortInput": [ - "Bitkit/Views/Settings/Advanced/ElectrumSettingsScreen.swift", - "Bitkit/Views/Transfer/FundManualSetupView.swift" - ], - "ExternalContinue": [ - "Bitkit/Views/Transfer/FundManualSetupView.swift" - ], - "ExternalAmount": [ - "Bitkit/Views/Transfer/FundManualAmountView.swift" - ], - "ExternalAmountAvailable": [ - "Bitkit/Views/Transfer/FundManualAmountView.swift" - ], - "N9": [ - "Bitkit/Components/NumberPad.swift" - ], - "ExternalAmountExceededToast": [ - "Bitkit/Views/Transfer/FundManualAmountView.swift" - ], - "ExternalAmountNumberField": [ - "Bitkit/Views/Transfer/FundManualAmountView.swift" - ], - "NRemove": [ - "Bitkit/Components/NumberPad.swift" - ] - }, - "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": [ - "Bitkit/Components/TabBar/TabBar.swift" - ], - "RecipientManual": [ - "Bitkit/Views/Wallets/Send/SendOptionsView.swift" - ], - "RecipientInput": [ - "Bitkit/Views/Wallets/Send/SendEnterManuallyView.swift" - ], - "AddressContinue": [ - "Bitkit/Views/Wallets/Send/SendEnterManuallyView.swift" - ], - "SendAmount": [ - "Bitkit/Views/Wallets/Send/SendAmountView.swift" - ], - "AvailableAmount": [ - "Bitkit/Views/Wallets/Send/SendAmountView.swift" - ], - "N9": [ - "Bitkit/Components/NumberPad.swift" - ], - "SendAmountExceededToast": [ - "Bitkit/Views/Wallets/LnurlWithdraw/LnurlWithdrawAmount.swift", - "Bitkit/Views/Wallets/Send/LnurlPayAmount.swift", - "Bitkit/Views/Wallets/Send/SendAmountView.swift" - ], - "SendNumberField": [ - "Bitkit/Views/Wallets/LnurlWithdraw/LnurlWithdrawAmount.swift", - "Bitkit/Views/Wallets/Send/LnurlPayAmount.swift", - "Bitkit/Views/Wallets/Send/SendAmountView.swift" - ], - "NRemove": [ - "Bitkit/Components/NumberPad.swift" - ] - }, - "screens": [ - "Send amount screen" - ] - }, - { - "file": "journeys/amount-limits/transfer-spending-advanced-over-max.xml", - "name": "receiving capacity amount over max is blocked", - "identifiers": { - "ActivitySavings": [ - "Bitkit/Views/Home/HomeWalletView.swift" - ], - "TransferToSpending": [ - "Bitkit/Views/Wallets/SavingsWalletScreen.swift" - ], - "SpendingAmount": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "SpendingAmountAvailable": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "SpendingAmountMax": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "SpendingAmountContinue": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "SpendingConfirmAdvanced": [ - "Bitkit/Views/Transfer/SpendingConfirm.swift" - ], - "SpendingAdvanced": [ - "Bitkit/Views/Transfer/SpendingAdvancedView.swift" - ], - "SpendingAdvancedMax": [ - "Bitkit/Views/Transfer/SpendingAdvancedView.swift" - ], - "N9": [ - "Bitkit/Components/NumberPad.swift" - ], - "SpendingAdvancedNumberField": [ - "Bitkit/Views/Transfer/SpendingAdvancedView.swift" - ], - "SpendingAdvancedExceededToast": [ - "Bitkit/Views/Transfer/SpendingAdvancedView.swift" - ], - "NRemove": [ - "Bitkit/Components/NumberPad.swift" - ] - }, - "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": { - "ActivitySavings": [ - "Bitkit/Views/Home/HomeWalletView.swift" - ], - "TransferToSpending": [ - "Bitkit/Views/Wallets/SavingsWalletScreen.swift" - ], - "SpendingAmount": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "SpendingAmountAvailable": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "N9": [ - "Bitkit/Components/NumberPad.swift" - ], - "SpendingAmountExceededToast": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "SpendingAmountNumberField": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "NRemove": [ - "Bitkit/Components/NumberPad.swift" - ] - }, - "screens": [ - "home screen", - "transfer intro screen", - "spending amount screen" - ] - }, - { - "file": "journeys/cjit-notifications/cjit-background-notification.xml", - "name": "cjit notification while backgrounded is single and correctly typed", - "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": [ - "Bitkit/ViewModels/AppViewModel.swift" - ] - }, - "screens": [ - "wallet home screen" - ] - }, - { - "file": "journeys/hardware-wallet/activity-blue-icons.xml", - "name": "Hardware Wallet Activity Blue Icons", - "identifiers": { - "ActivityShowAll": [ - "Bitkit/Views/Wallets/Activity/ActivityLatest.swift" - ], - "Tab-sent": [ - "Bitkit/Components/SegmentedControl.swift" - ], - "Tab-received": [ - "Bitkit/Components/SegmentedControl.swift" - ], - "Tab-other": [ - "Bitkit/Components/SegmentedControl.swift" - ], - "Tab-all": [ - "Bitkit/Components/SegmentedControl.swift" - ] - }, - "screens": [ - "wallet home screen", - "detail screen" - ] - }, - { - "file": "journeys/hardware-wallet/activity-detail-hw-tags.xml", - "name": "Hardware Wallet Activity Tags And Inputs Outputs", - "identifiers": { - "ActivityShowAll": [ - "Bitkit/Views/Wallets/Activity/ActivityLatest.swift" - ], - "ActivityTag": [ - "Bitkit/Views/Wallets/Activity/ActivityItemView.swift" - ], - "TagInput": [ - "Bitkit/Views/Wallets/Sheets/AddTagSheet.swift" - ], - "ActivityTagsSubmit": [ - "Bitkit/Views/Wallets/Sheets/AddTagSheet.swift" - ], - "ActivityTags": [ - "Bitkit/Views/Wallets/Activity/ActivityItemView.swift" - ], - "ActivityTxDetails": [ - "Bitkit/Views/Wallets/Activity/ActivityItemView.swift" - ], - "TagsPrompt": [ - "Bitkit/Components/Activity/ActivityListFilter.swift" - ] - }, - "screens": [ - "wallet home screen", - "activity detail screen", - "detail screen", - "Activity Explorer screen" - ] - }, - { - "file": "journeys/hardware-wallet/connect-flow.xml", - "name": "Hardware Wallet Connect Flow From Settings", - "identifiers": { - "TrezorForgetDevice-bridge": [ - "Bitkit/Components/Trezor/TrezorDeviceRow.swift" - ], - "Tab-general": [ - "Bitkit/Components/SegmentedControl.swift" - ], - "HardwareWalletsSettings": [ - "Bitkit/Views/Settings/GeneralSettingsView.swift" - ], - "HardwareWalletsScreen": [ - "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" - ], - "AddHardwareWallet": [ - "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" - ], - "HardwareWalletIntroScreen": [ - "Bitkit/Views/Sheets/HardwareConnectSheet.swift" - ], - "HardwareWalletIntroContinue": [ - "Bitkit/Views/Sheets/HardwareConnectSheet.swift" - ], - "HardwareWalletSearchingScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwSearchingView.swift" - ], - "HardwareWalletFoundScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" - ], - "HardwareWalletFoundConnect": [ - "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" - ], - "HardwareWalletPairedScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ], - "HardwareWalletLabelInput": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ], - "HardwareWalletPairedFinish": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ] - }, - "screens": [ - "Hardware Wallets screen" - ] - }, - { - "file": "journeys/hardware-wallet/connect-home-tile.xml", - "name": "Hardware Wallet Connect and Home Tile", - "identifiers": { - "DrawerSettings": [ - "Bitkit/Components/DrawerView.swift" - ], - "TrezorScanButton": [ - "Bitkit/Views/Trezor/TrezorDeviceListView.swift" - ], - "TrezorDevice-bridge": [ - "Bitkit/Components/Trezor/TrezorDeviceRow.swift" - ], - "ActivityHardware": [ - "Bitkit/Components/HardwareWalletsGrid.swift" - ], - "HardwareWalletScreen": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ] - }, - "screens": [ - "wallet home screen", - "hardware wallet detail screen" - ] - }, - { - "file": "journeys/hardware-wallet/detail-overview.xml", - "name": "Hardware Wallet Detail Overview", - "identifiers": { - "ActivityHardware": [ - "Bitkit/Components/HardwareWalletsGrid.swift" - ], - "HardwareWalletScreen": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferToSpending": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferAmount": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "RemoveHardwareWallet": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "RemoveHwWalletDialog": [ - "Bitkit/Components/RemoveHwWalletDialog.swift" - ] - }, - "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": { - "ActivityHardware": [ - "Bitkit/Components/HardwareWalletsGrid.swift" - ], - "Tab-general": [ - "Bitkit/Components/SegmentedControl.swift" - ], - "HardwareWalletsSettings": [ - "Bitkit/Views/Settings/GeneralSettingsView.swift" - ], - "AddHardwareWallet": [ - "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" - ], - "HardwareWalletIntroContinue": [ - "Bitkit/Views/Sheets/HardwareConnectSheet.swift" - ], - "HardwareWalletFoundConnect": [ - "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" - ], - "HardwareWalletPairedPassphrase": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ], - "HardwareWalletPassphraseInput": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" - ], - "HardwareWalletPassphraseContinue": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" - ], - "HwPassphraseError": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" - ], - "HardwareWalletPassphraseScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" - ] - }, - "screens": [ - "wallet home screen" - ] - }, - { - "file": "journeys/hardware-wallet/passphrase-pairing.xml", - "name": "Hardware Wallet Passphrase Pairing", - "identifiers": { - "Tab-general": [ - "Bitkit/Components/SegmentedControl.swift" - ], - "HardwareWalletsSettings": [ - "Bitkit/Views/Settings/GeneralSettingsView.swift" - ], - "AddHardwareWallet": [ - "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" - ], - "HardwareWalletIntroContinue": [ - "Bitkit/Views/Sheets/HardwareConnectSheet.swift" - ], - "HardwareWalletFoundScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" - ], - "HardwareWalletFoundConnect": [ - "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" - ], - "HardwareWalletPairedScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ], - "HardwareWalletPairedPassphrase": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ], - "HardwareWalletPairedFinish": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ], - "HardwareWalletPassphraseScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" - ], - "HardwareWalletPassphraseContinue": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" - ], - "HardwareWalletPassphraseInput": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift" - ], - "HardwareWalletPassphrasePairedScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ], - "HardwareWalletLabelInput": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ], - "HardwareWalletScreen": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ] - }, - "screens": [ - "wallet home screen", - "hardware wallet detail screen" - ] - }, - { - "file": "journeys/hardware-wallet/passphrase-settings-remove.xml", - "name": "Hardware Wallet Passphrase Settings And Remove", - "identifiers": { - "DrawerSettings": [ - "Bitkit/Components/DrawerView.swift" - ], - "Tab-general": [ - "Bitkit/Components/SegmentedControl.swift" - ], - "HardwareWalletsSettings": [ - "Bitkit/Views/Settings/GeneralSettingsView.swift" - ], - "HardwareWalletsScreen": [ - "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" - ], - "RenameHardwareWalletInput": [ - "Bitkit/Views/Sheets/RenameHardwareWalletSheet.swift" - ], - "RenameHardwareWalletSave": [ - "Bitkit/Views/Sheets/RenameHardwareWalletSheet.swift" - ], - "RemoveHwWalletDialog": [ - "Bitkit/Components/RemoveHwWalletDialog.swift" - ] - }, - "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": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferToSpending": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferAmountQuarter": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferAmountContinue": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferSign": [ - "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" - ], - "HardwareTransferOpenTrezorConnect": [ - "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" - ], - "HardwareTransferSigned": [ - "Bitkit/Views/Transfer/Hardware/SpendingHwSigned.swift" - ], - "HwTransferPassphraseSheet": [ - "Bitkit/Views/Transfer/Hardware/HwPassphrasePromptSheet.swift" - ], - "HwTransferPassphraseInput": [ - "Bitkit/Views/Transfer/Hardware/HwPassphrasePromptSheet.swift" - ], - "HwTransferPassphraseContinue": [ - "Bitkit/Views/Transfer/Hardware/HwPassphrasePromptSheet.swift" - ] - }, - "screens": [ - "wallet home screen", - "detail screen", - "sign screen", - "transaction signed screen", - "Processing Payment screen", - "home screen", - "passphrase sheet" - ] - }, - { - "file": "journeys/hardware-wallet/reconnect.xml", - "name": "Hardware Wallet Reconnect", - "identifiers": { - "TrezorDisconnectButton": [ - "Bitkit/Views/Trezor/TrezorConnectedView.swift" - ], - "ActivityHardware": [ - "Bitkit/Components/HardwareWalletsGrid.swift" - ], - "TrezorKnownDeviceConnect-bridge": [ - "Bitkit/Components/Trezor/TrezorDeviceRow.swift" - ] - }, - "screens": [ - "wallet home screen" - ] - }, - { - "file": "journeys/hardware-wallet/settings-hardware-wallets.xml", - "name": "Hardware Wallet Settings", - "identifiers": { - "DrawerSettings": [ - "Bitkit/Components/DrawerView.swift" - ], - "Tab-general": [ - "Bitkit/Components/SegmentedControl.swift" - ], - "HardwareWalletsSettings": [ - "Bitkit/Views/Settings/GeneralSettingsView.swift" - ], - "HardwareWalletsScreen": [ - "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" - ], - "RenameHardwareWalletInput": [ - "Bitkit/Views/Sheets/RenameHardwareWalletSheet.swift" - ], - "RenameHardwareWalletSave": [ - "Bitkit/Views/Sheets/RenameHardwareWalletSheet.swift" - ], - "AddHardwareWallet": [ - "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift" - ], - "HardwareWalletIntroScreen": [ - "Bitkit/Views/Sheets/HardwareConnectSheet.swift" - ], - "HardwareWalletIntroCancel": [ - "Bitkit/Views/Sheets/HardwareConnectSheet.swift" - ], - "RemoveHwWalletDialog": [ - "Bitkit/Components/RemoveHwWalletDialog.swift" - ], - "TrezorScanButton": [ - "Bitkit/Views/Trezor/TrezorDeviceListView.swift" - ] - }, - "screens": [ - "Hardware Wallets screen", - "Rename Hardware Wallet sheet" - ] - }, - { - "file": "journeys/hardware-wallet/suggestion-intro-sheet.xml", - "name": "Hardware Wallet Suggestion Card and Intro Sheet", - "identifiers": { - "TrezorForgetDevice-bridge": [ - "Bitkit/Components/Trezor/TrezorDeviceRow.swift" - ], - "ActivityHardware": [ - "Bitkit/Components/HardwareWalletsGrid.swift" - ], - "HardwareWalletIntroScreen": [ - "Bitkit/Views/Sheets/HardwareConnectSheet.swift" - ], - "HardwareWalletIntroContinue": [ - "Bitkit/Views/Sheets/HardwareConnectSheet.swift" - ], - "HardwareWalletSearchingScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwSearchingView.swift" - ], - "HardwareWalletSearchingCancel": [ - "Bitkit/Views/Sheets/HardwareConnect/HwSearchingView.swift" - ], - "HardwareWalletFoundScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" - ], - "HardwareWalletFoundConnect": [ - "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift" - ], - "HardwareWalletPairedScreen": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ], - "HardwareWalletLabelInput": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ], - "HardwareWalletPairedFinish": [ - "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift" - ] - }, - "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": { - "ActivityHardware": [ - "Bitkit/Components/HardwareWalletsGrid.swift" - ], - "HardwareWalletScreen": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferToSpending": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferAmount": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferAmountAvailable": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferAmountMax": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferAmountContinue": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferSign": [ - "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" - ], - "HardwareTransferOpenTrezorConnect": [ - "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" - ], - "HardwareTransferSigned": [ - "Bitkit/Views/Transfer/Hardware/SpendingHwSigned.swift" - ] - }, - "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": { - "ActivityHardware": [ - "Bitkit/Components/HardwareWalletsGrid.swift" - ], - "HardwareTransferToSpending": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferAmountQuarter": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferAmountContinue": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferSign": [ - "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" - ] - }, - "screens": [ - "wallet home screen", - "transfer amount screen", - "amount screen", - "sign screen" - ] - }, - { - "file": "journeys/hardware-wallet/transfer-to-spending-over-max.xml", - "name": "hardware transfer to spending amount over max is blocked", - "identifiers": { - "ActivityHardware": [ - "Bitkit/Components/HardwareWalletsGrid.swift" - ], - "HardwareWalletScreen": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferToSpending": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferAmount": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "N9": [ - "Bitkit/Components/NumberPad.swift" - ], - "HardwareTransferAmountAvailable": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferAmountExceededToast": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "NRemove": [ - "Bitkit/Components/NumberPad.swift" - ], - "NavigationBack": [ - "Bitkit/Components/NavigationBar.swift", - "Bitkit/Views/Security/AuthCheck.swift", - "Bitkit/Views/Sheets/Sheet.swift" - ] - }, - "screens": [ - "hardware wallet detail screen", - "transfer amount screen", - "hardware wallet screen" - ] - }, - { - "file": "journeys/hardware-wallet/transfer-to-spending.xml", - "name": "Hardware Wallet Transfer To Spending", - "identifiers": { - "ActivityHardware": [ - "Bitkit/Components/HardwareWalletsGrid.swift" - ], - "HardwareWalletScreen": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferToSpending": [ - "Bitkit/Views/Wallets/HardwareWalletScreen.swift" - ], - "HardwareTransferAmount": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferAmountAvailable": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferAmountQuarter": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferAmountContinue": [ - "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift" - ], - "HardwareTransferSign": [ - "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" - ], - "HardwareTransferOpenTrezorConnect": [ - "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift" - ], - "HardwareTransferSigned": [ - "Bitkit/Views/Transfer/Hardware/SpendingHwSigned.swift" - ] - }, - "screens": [ - "wallet home screen", - "hardware wallet detail screen", - "transfer amount screen", - "sign screen", - "transaction signed screen", - "detail screen" - ] - }, - { - "file": "journeys/notification-permission/receive-cjit-confirm-notification-toggle.xml", - "name": "receive cjit confirm toggle requests notification permission", - "identifiers": { - "Receive": [ - "Bitkit/Components/TabBar/TabBar.swift" - ], - "ReceiveCjitAmount": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" - ], - "ReceiveCjitAmountContinue": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" - ], - "ReceiveCjitConfirm": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift" - ], - "ReceiveConfirmNotificationSwitch": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift" - ] - }, - "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": { - "Receive": [ - "Bitkit/Components/TabBar/TabBar.swift" - ], - "ReceiveCjitAmount": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" - ], - "ReceiveCjitAmountContinue": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" - ], - "ReceiveCjitLiquidity": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitLearnMore.swift" - ], - "ReceiveLiquidityNotificationSwitch": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitLearnMore.swift" - ] - }, - "screens": [ - "home screen", - "Receive sheet", - "amount screen", - "confirm screen", - "liquidity screen" - ] - }, - { - "file": "journeys/notification-permission/toggle-off-and-system-settings-route.xml", - "name": "notification toggle off unregisters and settings routes to system", - "identifiers": { - "Receive": [ - "Bitkit/Components/TabBar/TabBar.swift" - ], - "ReceiveCjitAmount": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" - ], - "ReceiveCjitAmountContinue": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift" - ], - "ReceiveCjitConfirm": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift" - ], - "ReceiveConfirmNotificationSwitch": [ - "Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift" - ], - "DrawerSettings": [ - "Bitkit/Components/DrawerView.swift" - ], - "NotificationsOpenSystemSettings": [ - "Bitkit/Views/Settings/Notifications/NotificationsSettings.swift" - ] - }, - "screens": [ - "home screen", - "Receive sheet", - "amount screen", - "confirm screen", - "wallet home screen", - "Notifications settings screen" - ] - }, - { - "file": "journeys/notification-permission/transfer-spending-confirm-notification-toggle.xml", - "name": "transfer spending confirm toggle requests notification permission", - "identifiers": { - "ActivitySavings": [ - "Bitkit/Views/Home/HomeWalletView.swift" - ], - "TransferToSpending": [ - "Bitkit/Views/Wallets/SavingsWalletScreen.swift" - ], - "SpendingAmount": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "SpendingAmountAvailable": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "SpendingAmountContinue": [ - "Bitkit/Views/Transfer/SpendingAmount.swift" - ], - "SpendingConfirmNotificationSwitch": [ - "Bitkit/Views/Transfer/SpendingConfirm.swift" - ] - }, - "screens": [ - "home screen", - "transfer intro screen", - "spending amount screen", - "spending confirm screen" - ] - }, - { - "file": "journeys/pubky-marketplace/wallet-leg.xml", - "name": "pubky marketplace wallet leg", - "identifiers": { - "PubkyAuthWatchOnlyConsent": [ - "Bitkit/Views/Sheets/PubkyAuthApproval/PubkyAuthApprovalSheet.swift" - ], - "PubkyAuthWatchOnlyApprove": [ - "Bitkit/Views/Sheets/PubkyAuthApproval/PubkyAuthApprovalSheet.swift" - ], - "PubkyAuthAuthorize": [ - "Bitkit/Views/Sheets/PubkyAuthApproval/PubkyAuthApprovalSheet.swift" - ], - "PubkyAuthOK": [ - "Bitkit/Views/Sheets/PubkyAuthApproval/PubkyAuthApprovalSheet.swift" - ], - "ContactPaymentsToggle": [ - "Bitkit/Views/Settings/GeneralSettingsView.swift" - ], - "Contact_": [ - "Bitkit/Views/Contacts/ContactsListView.swift" - ], - "Contact_": [ - "Bitkit/Views/Contacts/ContactsListView.swift" - ], - "ReviewAmount": [ - "Bitkit/Views/Wallets/Send/LnurlPayConfirm.swift", - "Bitkit/Views/Wallets/Send/SendConfirmationView.swift" - ], - "PaymentRequestsBell": [ - "Bitkit/Components/Header.swift" - ], - "PaymentRequestsSheet": [ - "Bitkit/Views/PaymentRequests/PaymentRequestsView.swift" - ], - "PaymentRequestRow----one-time": [ - "Bitkit/Views/PaymentRequests/PaymentRequestsView.swift" - ], - "PaymentRequestPay-": [], - "ReviewContactRecipient": [ - "Bitkit/Views/Wallets/Send/SendConfirmationView.swift" - ], - "GRAB": [ - "Bitkit/Components/SwipeButton.swift" - ], - "SendSuccess": [ - "Bitkit/Views/Wallets/Send/SendSuccess.swift" - ], - "ActivityAmount": [ - "Bitkit/Views/Wallets/Activity/ActivityItemView.swift" - ], - "ActivityTxDetails": [ - "Bitkit/Views/Wallets/Activity/ActivityItemView.swift" - ] - }, - "screens": [ - "watch-only consent screen", - "authorization screen", - "incoming requests sheet" - ] - }, - { - "file": "journeys/widgets/add-widgets-flow.xml", - "name": "add widgets flow", - "identifiers": { - "DrawerWidgets": [ - "Bitkit/Components/DrawerView.swift" - ], - "WidgetsAdd": [ - "Bitkit/Views/Home/HomeWidgetsView.swift" - ], - "WidgetListItem-calculator": [ - "Bitkit/Views/Widgets/WidgetsListSheetView.swift" - ], - "WidgetSave": [ - "Bitkit/Views/Widgets/WidgetPreviewSheetView.swift" - ] - }, - "screens": [ - "Add Widget sheet" - ] - }, - { - "file": "journeys/widgets/widgets-intro.xml", - "name": "widgets intro", - "identifiers": { - "DrawerWidgets": [ - "Bitkit/Components/DrawerView.swift" - ], - "WidgetsOnboarding": [ - "Bitkit/Views/Widgets/WidgetsIntroView.swift" - ], - "WidgetsOnboardingAddWidget": [ - "Bitkit/Views/Widgets/WidgetsIntroView.swift" - ], - "WidgetListItem-weather": [ - "Bitkit/Views/Widgets/WidgetsListSheetView.swift" - ], - "WidgetSave": [ - "Bitkit/Views/Widgets/WidgetPreviewSheetView.swift" - ] - }, - "screens": [ - "widgets intro screen", - "Add Widget 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", + "ExternalAmountAvailable", + "N9", + "ExternalAmountExceededToast", + "ExternalAmountNumberField", + "NRemove" + ], + "sources": [ + "Bitkit/Components/NumberPad.swift", + "Bitkit/Views/Settings/Advanced/ElectrumSettingsScreen.swift", + "Bitkit/Views/Transfer/FundAdvancedOptions.swift", + "Bitkit/Views/Transfer/FundManualAmountView.swift", + "Bitkit/Views/Transfer/FundManualSetupView.swift", + "Bitkit/Views/Transfer/FundingOptions.swift" + ], + "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", + "SendAmount", + "AvailableAmount", + "N9", + "SendAmountExceededToast", + "SendNumberField", + "NRemove" + ], + "sources": [ + "Bitkit/Components/NumberPad.swift", + "Bitkit/Components/TabBar/TabBar.swift", + "Bitkit/Views/Wallets/LnurlWithdraw/LnurlWithdrawAmount.swift", + "Bitkit/Views/Wallets/Send/LnurlPayAmount.swift", + "Bitkit/Views/Wallets/Send/SendAmountView.swift", + "Bitkit/Views/Wallets/Send/SendEnterManuallyView.swift", + "Bitkit/Views/Wallets/Send/SendOptionsView.swift" + ], + "screens": [ + "Send amount screen" + ] + }, + { + "file": "journeys/amount-limits/transfer-spending-advanced-over-max.xml", + "name": "receiving capacity amount over max is blocked", + "identifiers": [ + "ActivitySavings", + "TransferToSpending", + "SpendingAmount", + "SpendingAmountAvailable", + "SpendingAmountMax", + "SpendingAmountContinue", + "SpendingConfirmAdvanced", + "SpendingAdvanced", + "SpendingAdvancedMax", + "N9", + "SpendingAdvancedNumberField", + "SpendingAdvancedExceededToast", + "NRemove" + ], + "sources": [ + "Bitkit/Components/NumberPad.swift", + "Bitkit/Views/Home/HomeWalletView.swift", + "Bitkit/Views/Transfer/SpendingAdvancedView.swift", + "Bitkit/Views/Transfer/SpendingAmount.swift", + "Bitkit/Views/Transfer/SpendingConfirm.swift", + "Bitkit/Views/Wallets/SavingsWalletScreen.swift" + ], + "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": [ + "ActivitySavings", + "TransferToSpending", + "SpendingAmount", + "SpendingAmountAvailable", + "N9", + "SpendingAmountExceededToast", + "SpendingAmountNumberField", + "NRemove" + ], + "sources": [ + "Bitkit/Components/NumberPad.swift", + "Bitkit/Views/Home/HomeWalletView.swift", + "Bitkit/Views/Transfer/SpendingAmount.swift", + "Bitkit/Views/Wallets/SavingsWalletScreen.swift" + ], + "screens": [ + "home screen", + "transfer intro screen", + "spending amount screen" + ] + }, + { + "file": "journeys/cjit-notifications/cjit-background-notification.xml", + "name": "cjit notification while backgrounded is single and correctly typed", + "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": [ + "Bitkit/ViewModels/AppViewModel.swift" + ], + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/hardware-wallet/activity-blue-icons.xml", + "name": "Hardware Wallet Activity Blue Icons", + "identifiers": [ + "ActivityShowAll", + "Tab-sent", + "Tab-received", + "Tab-other", + "Tab-all" + ], + "sources": [ + "Bitkit/Components/SegmentedControl.swift", + "Bitkit/Views/Wallets/Activity/ActivityLatest.swift" + ], + "screens": [ + "wallet home screen", + "detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/activity-detail-hw-tags.xml", + "name": "Hardware Wallet Activity Tags And Inputs Outputs", + "identifiers": [ + "ActivityShowAll", + "ActivityTag", + "TagInput", + "ActivityTagsSubmit", + "ActivityTags", + "ActivityTxDetails", + "TagsPrompt" + ], + "sources": [ + "Bitkit/Components/Activity/ActivityListFilter.swift", + "Bitkit/Views/Wallets/Activity/ActivityItemView.swift", + "Bitkit/Views/Wallets/Activity/ActivityLatest.swift", + "Bitkit/Views/Wallets/Sheets/AddTagSheet.swift" + ], + "screens": [ + "wallet home screen", + "activity detail screen", + "detail screen", + "Activity Explorer screen" + ] + }, + { + "file": "journeys/hardware-wallet/connect-flow.xml", + "name": "Hardware Wallet Connect Flow From Settings", + "identifiers": [ + "TrezorForgetDevice-bridge", + "Tab-general", + "HardwareWalletsSettings", + "HardwareWalletsScreen", + "AddHardwareWallet", + "HardwareWalletIntroScreen", + "HardwareWalletIntroContinue", + "HardwareWalletSearchingScreen", + "HardwareWalletFoundScreen", + "HardwareWalletFoundConnect", + "HardwareWalletPairedScreen", + "HardwareWalletLabelInput", + "HardwareWalletPairedFinish" + ], + "sources": [ + "Bitkit/Components/SegmentedControl.swift", + "Bitkit/Components/Trezor/TrezorDeviceRow.swift", + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift", + "Bitkit/Views/Settings/GeneralSettingsView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwSearchingView.swift", + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "screens": [ + "Hardware Wallets screen" + ] + }, + { + "file": "journeys/hardware-wallet/connect-home-tile.xml", + "name": "Hardware Wallet Connect and Home Tile", + "identifiers": [ + "DrawerSettings", + "TrezorScanButton", + "TrezorDevice-bridge", + "ActivityHardware", + "HardwareWalletScreen" + ], + "sources": [ + "Bitkit/Components/DrawerView.swift", + "Bitkit/Components/HardwareWalletsGrid.swift", + "Bitkit/Components/Trezor/TrezorDeviceRow.swift", + "Bitkit/Views/Trezor/TrezorDeviceListView.swift", + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "screens": [ + "wallet home screen", + "hardware wallet detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/detail-overview.xml", + "name": "Hardware Wallet Detail Overview", + "identifiers": [ + "ActivityHardware", + "HardwareWalletScreen", + "HardwareTransferToSpending", + "HardwareTransferAmount", + "RemoveHardwareWallet", + "RemoveHwWalletDialog" + ], + "sources": [ + "Bitkit/Components/HardwareWalletsGrid.swift", + "Bitkit/Components/RemoveHwWalletDialog.swift", + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift", + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "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": [ + "ActivityHardware", + "Tab-general", + "HardwareWalletsSettings", + "AddHardwareWallet", + "HardwareWalletIntroContinue", + "HardwareWalletFoundConnect", + "HardwareWalletPairedPassphrase", + "HardwareWalletPassphraseInput", + "HardwareWalletPassphraseContinue", + "HwPassphraseError", + "HardwareWalletPassphraseScreen" + ], + "sources": [ + "Bitkit/Components/HardwareWalletsGrid.swift", + "Bitkit/Components/SegmentedControl.swift", + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift", + "Bitkit/Views/Settings/GeneralSettingsView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift", + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-pairing.xml", + "name": "Hardware Wallet Passphrase Pairing", + "identifiers": [ + "Tab-general", + "HardwareWalletsSettings", + "AddHardwareWallet", + "HardwareWalletIntroContinue", + "HardwareWalletFoundScreen", + "HardwareWalletFoundConnect", + "HardwareWalletPairedScreen", + "HardwareWalletPairedPassphrase", + "HardwareWalletPairedFinish", + "HardwareWalletPassphraseScreen", + "HardwareWalletPassphraseContinue", + "HardwareWalletPassphraseInput", + "HardwareWalletPassphrasePairedScreen", + "HardwareWalletLabelInput", + "HardwareWalletScreen" + ], + "sources": [ + "Bitkit/Components/SegmentedControl.swift", + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift", + "Bitkit/Views/Settings/GeneralSettingsView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwPassphraseView.swift", + "Bitkit/Views/Sheets/HardwareConnectSheet.swift", + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "screens": [ + "wallet home screen", + "hardware wallet detail screen" + ] + }, + { + "file": "journeys/hardware-wallet/passphrase-settings-remove.xml", + "name": "Hardware Wallet Passphrase Settings And Remove", + "identifiers": [ + "DrawerSettings", + "Tab-general", + "HardwareWalletsSettings", + "HardwareWalletsScreen", + "RenameHardwareWalletInput", + "RenameHardwareWalletSave", + "RemoveHwWalletDialog" + ], + "sources": [ + "Bitkit/Components/DrawerView.swift", + "Bitkit/Components/RemoveHwWalletDialog.swift", + "Bitkit/Components/SegmentedControl.swift", + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift", + "Bitkit/Views/Settings/GeneralSettingsView.swift", + "Bitkit/Views/Sheets/RenameHardwareWalletSheet.swift" + ], + "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": [ + "Bitkit/Views/Transfer/Hardware/HwPassphrasePromptSheet.swift", + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift", + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift", + "Bitkit/Views/Transfer/Hardware/SpendingHwSigned.swift", + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "screens": [ + "wallet home screen", + "detail screen", + "sign screen", + "transaction signed screen", + "Processing Payment screen", + "home screen", + "passphrase sheet" + ] + }, + { + "file": "journeys/hardware-wallet/reconnect.xml", + "name": "Hardware Wallet Reconnect", + "identifiers": [ + "TrezorDisconnectButton", + "ActivityHardware", + "TrezorKnownDeviceConnect-bridge" + ], + "sources": [ + "Bitkit/Components/HardwareWalletsGrid.swift", + "Bitkit/Components/Trezor/TrezorDeviceRow.swift", + "Bitkit/Views/Trezor/TrezorConnectedView.swift" + ], + "screens": [ + "wallet home screen" + ] + }, + { + "file": "journeys/hardware-wallet/settings-hardware-wallets.xml", + "name": "Hardware Wallet Settings", + "identifiers": [ + "DrawerSettings", + "Tab-general", + "HardwareWalletsSettings", + "HardwareWalletsScreen", + "RenameHardwareWalletInput", + "RenameHardwareWalletSave", + "AddHardwareWallet", + "HardwareWalletIntroScreen", + "HardwareWalletIntroCancel", + "RemoveHwWalletDialog", + "TrezorScanButton" + ], + "sources": [ + "Bitkit/Components/DrawerView.swift", + "Bitkit/Components/RemoveHwWalletDialog.swift", + "Bitkit/Components/SegmentedControl.swift", + "Bitkit/Views/Settings/General/HardwareWalletsSettingsScreen.swift", + "Bitkit/Views/Settings/GeneralSettingsView.swift", + "Bitkit/Views/Sheets/HardwareConnectSheet.swift", + "Bitkit/Views/Sheets/RenameHardwareWalletSheet.swift", + "Bitkit/Views/Trezor/TrezorDeviceListView.swift" + ], + "screens": [ + "Hardware Wallets screen", + "Rename Hardware Wallet sheet" + ] + }, + { + "file": "journeys/hardware-wallet/suggestion-intro-sheet.xml", + "name": "Hardware Wallet Suggestion Card and Intro Sheet", + "identifiers": [ + "TrezorForgetDevice-bridge", + "ActivityHardware", + "HardwareWalletIntroScreen", + "HardwareWalletIntroContinue", + "HardwareWalletSearchingScreen", + "HardwareWalletSearchingCancel", + "HardwareWalletFoundScreen", + "HardwareWalletFoundConnect", + "HardwareWalletPairedScreen", + "HardwareWalletLabelInput", + "HardwareWalletPairedFinish" + ], + "sources": [ + "Bitkit/Components/HardwareWalletsGrid.swift", + "Bitkit/Components/Trezor/TrezorDeviceRow.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwFoundView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwPairedView.swift", + "Bitkit/Views/Sheets/HardwareConnect/HwSearchingView.swift", + "Bitkit/Views/Sheets/HardwareConnectSheet.swift" + ], + "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": [ + "ActivityHardware", + "HardwareWalletScreen", + "HardwareTransferToSpending", + "HardwareTransferAmount", + "HardwareTransferAmountAvailable", + "HardwareTransferAmountMax", + "HardwareTransferAmountContinue", + "HardwareTransferSign", + "HardwareTransferOpenTrezorConnect", + "HardwareTransferSigned" + ], + "sources": [ + "Bitkit/Components/HardwareWalletsGrid.swift", + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift", + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift", + "Bitkit/Views/Transfer/Hardware/SpendingHwSigned.swift", + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "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": [ + "ActivityHardware", + "HardwareTransferToSpending", + "HardwareTransferAmountQuarter", + "HardwareTransferAmountContinue", + "HardwareTransferSign" + ], + "sources": [ + "Bitkit/Components/HardwareWalletsGrid.swift", + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift", + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift", + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "screens": [ + "wallet home screen", + "transfer amount screen", + "amount screen", + "sign screen" + ] + }, + { + "file": "journeys/hardware-wallet/transfer-to-spending-over-max.xml", + "name": "hardware transfer to spending amount over max is blocked", + "identifiers": [ + "ActivityHardware", + "HardwareWalletScreen", + "HardwareTransferToSpending", + "HardwareTransferAmount", + "N9", + "HardwareTransferAmountAvailable", + "HardwareTransferAmountExceededToast", + "NRemove", + "NavigationBack" + ], + "sources": [ + "Bitkit/Components/HardwareWalletsGrid.swift", + "Bitkit/Components/NavigationBar.swift", + "Bitkit/Components/NumberPad.swift", + "Bitkit/Views/Security/AuthCheck.swift", + "Bitkit/Views/Sheets/Sheet.swift", + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift", + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "screens": [ + "hardware wallet detail screen", + "transfer amount screen", + "hardware wallet screen" + ] + }, + { + "file": "journeys/hardware-wallet/transfer-to-spending.xml", + "name": "Hardware Wallet Transfer To Spending", + "identifiers": [ + "ActivityHardware", + "HardwareWalletScreen", + "HardwareTransferToSpending", + "HardwareTransferAmount", + "HardwareTransferAmountAvailable", + "HardwareTransferAmountQuarter", + "HardwareTransferAmountContinue", + "HardwareTransferSign", + "HardwareTransferOpenTrezorConnect", + "HardwareTransferSigned" + ], + "sources": [ + "Bitkit/Components/HardwareWalletsGrid.swift", + "Bitkit/Views/Transfer/Hardware/SpendingAmountHw.swift", + "Bitkit/Views/Transfer/Hardware/SpendingHwSign.swift", + "Bitkit/Views/Transfer/Hardware/SpendingHwSigned.swift", + "Bitkit/Views/Wallets/HardwareWalletScreen.swift" + ], + "screens": [ + "wallet home screen", + "hardware wallet detail screen", + "transfer amount screen", + "sign screen", + "transaction signed screen", + "detail screen" + ] + }, + { + "file": "journeys/notification-permission/receive-cjit-confirm-notification-toggle.xml", + "name": "receive cjit confirm toggle requests notification permission", + "identifiers": [ + "Receive", + "ReceiveCjitAmount", + "ReceiveCjitAmountContinue", + "ReceiveCjitConfirm", + "ReceiveConfirmNotificationSwitch" + ], + "sources": [ + "Bitkit/Components/TabBar/TabBar.swift", + "Bitkit/Views/Settings/DevSettings/LegacyRnRecoveryScreen.swift", + "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift", + "Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift" + ], + "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": [ + "Receive", + "ReceiveCjitAmount", + "ReceiveCjitAmountContinue", + "ReceiveCjitLiquidity", + "ReceiveLiquidityNotificationSwitch" + ], + "sources": [ + "Bitkit/Components/TabBar/TabBar.swift", + "Bitkit/Views/Settings/DevSettings/LegacyRnRecoveryScreen.swift", + "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift", + "Bitkit/Views/Wallets/Receive/ReceiveCjitLearnMore.swift" + ], + "screens": [ + "home screen", + "Receive sheet", + "amount screen", + "confirm screen", + "liquidity screen" + ] + }, + { + "file": "journeys/notification-permission/toggle-off-and-system-settings-route.xml", + "name": "notification toggle off unregisters and settings routes to system", + "identifiers": [ + "Receive", + "ReceiveCjitAmount", + "ReceiveCjitAmountContinue", + "ReceiveCjitConfirm", + "ReceiveConfirmNotificationSwitch", + "DrawerSettings", + "NotificationsOpenSystemSettings" + ], + "sources": [ + "Bitkit/Components/DrawerView.swift", + "Bitkit/Components/TabBar/TabBar.swift", + "Bitkit/Views/Settings/DevSettings/LegacyRnRecoveryScreen.swift", + "Bitkit/Views/Settings/Notifications/NotificationsSettings.swift", + "Bitkit/Views/Wallets/Receive/ReceiveCjitAmount.swift", + "Bitkit/Views/Wallets/Receive/ReceiveCjitConfirmation.swift" + ], + "screens": [ + "home screen", + "Receive sheet", + "amount screen", + "confirm screen", + "wallet home screen", + "Notifications settings screen" + ] + }, + { + "file": "journeys/notification-permission/transfer-spending-confirm-notification-toggle.xml", + "name": "transfer spending confirm toggle requests notification permission", + "identifiers": [ + "ActivitySavings", + "TransferToSpending", + "SpendingAmount", + "SpendingAmountAvailable", + "SpendingAmountContinue", + "SpendingConfirmNotificationSwitch" + ], + "sources": [ + "Bitkit/Views/Home/HomeWalletView.swift", + "Bitkit/Views/Transfer/SpendingAmount.swift", + "Bitkit/Views/Transfer/SpendingConfirm.swift", + "Bitkit/Views/Wallets/SavingsWalletScreen.swift" + ], + "screens": [ + "home screen", + "transfer intro screen", + "spending amount screen", + "spending confirm screen" + ] + }, + { + "file": "journeys/pubky-marketplace/wallet-leg.xml", + "name": "pubky marketplace wallet leg", + "identifiers": [ + "PubkyAuthWatchOnlyConsent", + "PubkyAuthWatchOnlyApprove", + "PubkyAuthAuthorize", + "PubkyAuthOK", + "ContactPaymentsToggle", + "Contact_", + "Contact_", + "ReviewAmount", + "PaymentRequestsBell", + "PaymentRequestsSheet", + "PaymentRequestRow----one-time", + "PaymentRequestPay-", + "ReviewContactRecipient", + "GRAB", + "SendSuccess", + "ActivityAmount", + "ActivityTxDetails" + ], + "sources": [ + "Bitkit/Components/Header.swift", + "Bitkit/Components/SwipeButton.swift", + "Bitkit/Views/Contacts/ContactsListView.swift", + "Bitkit/Views/PaymentRequests/PaymentRequestsView.swift", + "Bitkit/Views/Settings/GeneralSettingsView.swift", + "Bitkit/Views/Sheets/PubkyAuthApproval/PubkyAuthApprovalSheet.swift", + "Bitkit/Views/Wallets/Activity/ActivityItemView.swift", + "Bitkit/Views/Wallets/Send/LnurlPayConfirm.swift", + "Bitkit/Views/Wallets/Send/SendConfirmationView.swift", + "Bitkit/Views/Wallets/Send/SendSuccess.swift" + ], + "screens": [ + "watch-only consent screen", + "authorization screen", + "incoming requests sheet" + ] + }, + { + "file": "journeys/widgets/add-widgets-flow.xml", + "name": "add widgets flow", + "identifiers": [ + "DrawerWidgets", + "WidgetsAdd", + "WidgetListItem-calculator", + "WidgetSave" + ], + "sources": [ + "Bitkit/Components/DrawerView.swift", + "Bitkit/Views/Home/HomeWidgetsView.swift", + "Bitkit/Views/Widgets/WidgetPreviewSheetView.swift", + "Bitkit/Views/Widgets/WidgetsListSheetView.swift" + ], + "screens": [ + "Add Widget sheet" + ] + }, + { + "file": "journeys/widgets/widgets-intro.xml", + "name": "widgets intro", + "identifiers": [ + "DrawerWidgets", + "WidgetsOnboarding", + "WidgetsOnboardingAddWidget", + "WidgetListItem-weather", + "WidgetSave" + ], + "sources": [ + "Bitkit/Components/DrawerView.swift", + "Bitkit/Views/Widgets/WidgetPreviewSheetView.swift", + "Bitkit/Views/Widgets/WidgetsIntroView.swift", + "Bitkit/Views/Widgets/WidgetsListSheetView.swift" + ], + "screens": [ + "widgets intro screen", + "Add Widget sheet" + ] + } +] diff --git a/scripts/journeys_index.py b/scripts/journeys_index.py index 5caa5e7dd..50dbdfc08 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 an accessibilityIdentifier("…") call for it; failing that, -to the files with a string literal equal to it (a labelled argument, a constant, a conditional -branch); failing that, to the files with the most specific string template that can produce it -("N\\(number)"). In a template with fewer than three characters of its own, an interpolation -matches no capital letters, so "N\\(number)" 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 id "Name"; Swift sources declare it. IDENTIFIER_MENTION = re.compile(r'\bid "([^"]+)"') SOURCE_DIRS = ("Bitkit", "BitkitNotification", "BitkitWidget") SOURCE_SUFFIX = ".swift" -STRING_LITERAL = re.compile(r'"((?:[^"\\\n]|\\\((?:[^()"\n]|"[^"\n]*"|\([^()\n]*\))*\)|\\.)*)"') -INTERPOLATION = re.compile(r'\\\((?:[^()"\n]|"[^"\n]*"|\([^()\n]*\))*\)') -DECLARATION_CALL = re.compile(r"\baccessibilityIdentifier\(\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) From 8596bbc5bc099a1937efc38d329e39809c230083 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 10 Sep 2026 21:19:27 +0200 Subject: [PATCH 4/4] chore: update journeys index --- journeys/index.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/journeys/index.json b/journeys/index.json index 7fafe5197..b2a6abedd 100644 --- a/journeys/index.json +++ b/journeys/index.json @@ -681,6 +681,28 @@ "spending confirm screen" ] }, + { + "file": "journeys/payment-requests/issuer-interoperability.xml", + "name": "Paykit Issuer Interoperability", + "identifiers": [ + "PaymentRequestConfirm", + "PaymentRequestsBell", + "PaymentRequestsSheet", + "PaymentRequestRow-71300000-0000-4000-8000-000000000001---one-time", + "PaymentRequestPay-71300000-0000-4000-8000-000000000001", + "SendConfirmToggleDetails", + "ReviewContactRecipient" + ], + "sources": [ + "Bitkit/Components/Header.swift", + "Bitkit/Views/PaymentRequests/PaymentRequestsView.swift", + "Bitkit/Views/Wallets/Send/SendConfirmationView.swift" + ], + "screens": [ + "Payment Request confirmation screen", + "incoming Payment Requests sheet" + ] + }, { "file": "journeys/pubky-marketplace/wallet-leg.xml", "name": "pubky marketplace wallet leg",