diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 5a802319..5904c480 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -107,7 +107,7 @@ jobs: # - { name: onchain_boost_receive_widgets, grep: "@onchain|@boost|@receive|@widgets" } # - { name: settings, grep: "@settings" } # - { name: security, grep: "@security" } - - { name: e2e, grep: "@onboarding|@onchain_1|@numberpad|@widgets" } + - { name: e2e, grep: "@onboarding|@onchain_1|@numberpad|@widgets|@boost" } name: e2e-tests - ${{ matrix.shard.name }} diff --git a/Bitkit/Components/NavigationBar.swift b/Bitkit/Components/NavigationBar.swift index a452b5ea..7d3683ad 100644 --- a/Bitkit/Components/NavigationBar.swift +++ b/Bitkit/Components/NavigationBar.swift @@ -48,6 +48,7 @@ struct NavigationBar: View { .foregroundColor(.textPrimary) .frame(width: 24, height: 24) } + .accessibilityIdentifier("NavigationBack") } else { Spacer() .frame(width: 24, height: 24) diff --git a/Bitkit/Components/SheetIntro.swift b/Bitkit/Components/SheetIntro.swift index 6953c7f1..2bb37c83 100644 --- a/Bitkit/Components/SheetIntro.swift +++ b/Bitkit/Components/SheetIntro.swift @@ -12,6 +12,9 @@ struct SheetIntro: View { let testID: String? let onCancel: (() -> Void)? let onContinue: () -> Void + private var baseTestID: String { + testID ?? "SheetIntro" + } init( navTitle: String, @@ -52,6 +55,7 @@ struct SheetIntro: View { .frame(width: UIScreen.main.bounds.width * 0.8) .frame(maxHeight: 256) .padding(.bottom, 32) + .accessibilityIdentifier("\(baseTestID)Image") DisplayText(title, accentColor: accentColor) .padding(.bottom, 14) @@ -59,13 +63,15 @@ struct SheetIntro: View { BodyMText(description, accentFont: accentFont) .frame(maxWidth: .infinity, alignment: .leading) + .accessibilityIdentifier("\(baseTestID)Description") } buttonStack .padding(.top, 32) } .padding(.horizontal, 32) - .accessibilityIdentifier(testID ?? "SheetIntro") + .accessibilityElement(children: .contain) + .accessibilityIdentifier(baseTestID) } @ViewBuilder @@ -78,12 +84,14 @@ struct SheetIntro: View { ) { onCancel() } + .accessibilityIdentifier("\(baseTestID)Cancel") CustomButton( title: continueText ) { onContinue() } + .accessibilityIdentifier("\(baseTestID)Continue") } } else { CustomButton( @@ -91,6 +99,7 @@ struct SheetIntro: View { ) { onContinue() } + .accessibilityIdentifier("\(baseTestID)Continue") } } } diff --git a/Bitkit/Views/Wallets/Activity/ActivityExplorerView.swift b/Bitkit/Views/Wallets/Activity/ActivityExplorerView.swift index ac894f91..b429d4fc 100644 --- a/Bitkit/Views/Wallets/Activity/ActivityExplorerView.swift +++ b/Bitkit/Views/Wallets/Activity/ActivityExplorerView.swift @@ -76,8 +76,15 @@ struct ActivityExplorerView: View { private struct InfoSection: View { let title: String let content: String + let testId: String? @EnvironmentObject var app: AppViewModel + init(title: String, content: String, testId: String? = nil) { + self.title = title + self.content = content + self.testId = testId + } + var body: some View { Button { UIPasteboard.general.string = content @@ -94,6 +101,7 @@ struct ActivityExplorerView: View { } } .buttonStyle(.plain) + .accessibilityIdentifierIfPresent(testId) } } @@ -112,7 +120,8 @@ struct ActivityExplorerView: View { if let onchain { InfoSection( title: t("wallet__activity_tx_id"), - content: onchain.txId + content: onchain.txId, + testId: "TXID" ) if let txDetails { @@ -153,7 +162,8 @@ struct ActivityExplorerView: View { InfoSection( title: t(key, variables: ["num": String(index + 1)]), - content: boostTxId + content: boostTxId, + testId: onchain.txType == .received ? "CPFPBoosted" : "RBFBoosted" ) } } diff --git a/Bitkit/Views/Wallets/Activity/ActivityIcon.swift b/Bitkit/Views/Wallets/Activity/ActivityIcon.swift index f8a67944..589a3a23 100644 --- a/Bitkit/Views/Wallets/Activity/ActivityIcon.swift +++ b/Bitkit/Views/Wallets/Activity/ActivityIcon.swift @@ -31,44 +31,54 @@ struct ActivityIcon: View { } var body: some View { - if isLightning { - if status == .failed { + Group { + if isLightning { + if status == .failed { + CircularIcon( + icon: "x-circle", + iconColor: .purpleAccent, + backgroundColor: .purple16, + size: size + ) + } else if status == .pending { + CircularIcon( + icon: "hourglass-simple", + iconColor: .purpleAccent, + backgroundColor: .purple16, + size: size + ) + } else { + CircularIcon( + icon: txType == .sent ? "arrow-up" : "arrow-down", + iconColor: .purpleAccent, + backgroundColor: .purple16, + size: size + ) + } + } else if isBoosted && !(confirmed ?? false) { CircularIcon( - icon: "x-circle", - iconColor: .purpleAccent, - backgroundColor: .purple16, - size: size - ) - } else if status == .pending { - CircularIcon( - icon: "hourglass-simple", - iconColor: .purpleAccent, - backgroundColor: .purple16, + icon: "timer-alt", + iconColor: .yellow, + backgroundColor: .yellow16, size: size ) } else { + let paymentIcon = txType == PaymentType.sent ? "arrow-up" : "arrow-down" CircularIcon( - icon: txType == .sent ? "arrow-up" : "arrow-down", - iconColor: .purpleAccent, - backgroundColor: .purple16, + icon: isTransfer ? "arrow-up-down" : paymentIcon, + iconColor: .brandAccent, + backgroundColor: .brand16, size: size ) } - } else if isBoosted && !(confirmed ?? false) { - CircularIcon( - icon: "timer-alt", - iconColor: .yellow, - backgroundColor: .yellow16, - size: size - ) - } else { - let paymentIcon = txType == PaymentType.sent ? "arrow-up" : "arrow-down" - CircularIcon( - icon: isTransfer ? "arrow-up-down" : paymentIcon, - iconColor: .brandAccent, - backgroundColor: .brand16, - size: size - ) } + .accessibilityIdentifierIfPresent(iconAccessibilityIdentifier) + } + + private var iconAccessibilityIdentifier: String? { + if !isLightning, isBoosted, !(confirmed ?? false) { + return "BoostingIcon" + } + return nil } } diff --git a/Bitkit/Views/Wallets/Activity/ActivityItemView.swift b/Bitkit/Views/Wallets/Activity/ActivityItemView.swift index 8baed47d..1d20d798 100644 --- a/Bitkit/Views/Wallets/Activity/ActivityItemView.swift +++ b/Bitkit/Views/Wallets/Activity/ActivityItemView.swift @@ -112,6 +112,21 @@ struct ActivityItemView: View { } } + private var statusAccessibilityIdentifier: String? { + switch viewModel.activity { + case let .onchain(activity): + if activity.confirmed == true { + return "StatusConfirmed" + } + if activity.isBoosted { + return "StatusBoosting" + } + return "StatusConfirming" + case .lightning: + return nil + } + } + var body: some View { VStack(alignment: .leading, spacing: 0) { NavigationBar(title: navigationTitle) @@ -124,7 +139,6 @@ struct ActivityItemView: View { Spacer() ActivityIcon(activity: viewModel.activity, size: 48) } - .accessibilityIdentifier("ActivityAmount") .padding(.bottom, 16) statusSection @@ -208,6 +222,7 @@ struct ActivityItemView: View { } } } + .accessibilityIdentifierIfPresent(statusAccessibilityIdentifier) .padding(.bottom, 16) Divider() @@ -263,8 +278,9 @@ struct ActivityItemView: View { Image("user") .foregroundColor(accentColor) .frame(width: 16, height: 16) - MoneyText(sats: Int(activity.value), size: .bodySSB) + MoneyText(sats: Int(activity.value), size: .bodySSB, testIdentifier: "MoneyText") } + .accessibilityElement(children: .contain) .accessibilityIdentifier("ActivityAmount") .padding(.bottom, 16) @@ -281,8 +297,9 @@ struct ActivityItemView: View { Image("timer") .foregroundColor(accentColor) .frame(width: 16, height: 16) - MoneyText(sats: Int(fee), size: .bodySSB) + MoneyText(sats: Int(fee), size: .bodySSB, testIdentifier: "MoneyText") } + .accessibilityElement(children: .contain) .accessibilityIdentifier("ActivityFee") .padding(.bottom, 16) diff --git a/Bitkit/Views/Wallets/Sheets/BoostSheet.swift b/Bitkit/Views/Wallets/Sheets/BoostSheet.swift index 40558675..b61f4a51 100644 --- a/Bitkit/Views/Wallets/Sheets/BoostSheet.swift +++ b/Bitkit/Views/Wallets/Sheets/BoostSheet.swift @@ -26,6 +26,7 @@ struct BoostSheet: View { @EnvironmentObject private var wallet: WalletViewModel @EnvironmentObject private var currency: CurrencyViewModel @EnvironmentObject private var activityList: ActivityListViewModel + @EnvironmentObject private var navigation: NavigationViewModel let config: BoostSheetItem @State private var feeRate: UInt32? @@ -115,6 +116,7 @@ struct BoostSheet: View { .cornerRadius(200) } .disabled(currentFeeRate <= minFeeRate) + .accessibilityIdentifier("Minus") Spacer() @@ -141,12 +143,14 @@ struct BoostSheet: View { .cornerRadius(200) } .disabled(currentFeeRate >= maxFeeRate) + .accessibilityIdentifier("Plus") } CustomButton(title: "Use Suggested Fee", size: .small) { isEditingFee = false editedFeeRate = nil } + .accessibilityIdentifier("RecommendedFeeButton") } .padding(.vertical, 12) .cornerRadius(12) @@ -203,6 +207,7 @@ struct BoostSheet: View { } .buttonStyle(PlainButtonStyle()) .disabled(feeRate == nil || fetchingFees) + .accessibilityIdentifier("CustomFeeButton") } .padding(.vertical, 12) .cornerRadius(12) @@ -219,6 +224,8 @@ struct BoostSheet: View { } } .padding(.horizontal, 16) + .accessibilityElement(children: .contain) + .accessibilityIdentifier(isIncoming ? "CPFPBoost" : "RBFBoost") } .onAppear { fetchFeeRate() @@ -391,6 +398,7 @@ struct BoostSheet: View { Logger.info("Boost transaction completed successfully, hiding sheet", context: "BoostSheet.performBoost") sheets.hideSheet() + navigation.reset() } catch { Logger.error("Failed to boost transaction: \(error)", context: "BoostSheet.performBoost")