[#709] Better error handling in tests (#713)

- Strings replaced with ZcashError
- unit test don't depend on error descriptions anymore

[#709] Better error handling in tests (#713)

- cleanup

[#709] Better error handling in tests (#713)

- alerts now show error message + code
This commit is contained in:
Lukas Korba 2023-05-10 18:52:56 +02:00 committed by GitHub
parent 647eeea7aa
commit c143f4b31d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 155 additions and 179 deletions

View File

@ -3651,7 +3651,7 @@
repositoryURL = "https://github.com/zcash/ZcashLightClientKit.git";
requirement = {
kind = revision;
revision = 336c5a7c382f363d27a28585c42d190e25c8bdee;
revision = 355a3fc4f506b400af254ac2188baff1854bd977;
};
};
6654C7382715A38000901167 /* XCRemoteSwiftPackageReference "swift-composable-architecture" */ = {

View File

@ -338,7 +338,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash/ZcashLightClientKit.git",
"state" : {
"revision" : "336c5a7c382f363d27a28585c42d190e25c8bdee"
"revision" : "355a3fc4f506b400af254ac2188baff1854bd977"
}
}
],

View File

@ -7,6 +7,7 @@
import Foundation
import ComposableArchitecture
import ZcashLightClientKit
extension RootReducer {
indirect enum AlertAction: Equatable {
@ -24,41 +25,41 @@ extension RootReducer {
enum AlertRequest: Equatable {
enum BalanceBreakdown: Equatable {
case shieldFundsSuccess
case shieldFundsFailure(String)
case shieldFundsFailure(ZcashError)
}
enum ExportLogs: Equatable {
case failed(String)
case failed(ZcashError)
}
enum Home: Equatable {
case syncFailed(String, String)
case syncFailed(ZcashError, String)
}
enum ImportWallet: Equatable {
case succeed
case failed(String)
case failed(ZcashError)
}
enum Root: Equatable {
case cantCreateNewWallet(String)
case cantCreateNewWallet(ZcashError)
case cantLoadSeedPhrase
case cantStartSync(String)
case cantStoreThatUserPassedPhraseBackupTest(String)
case failedToProcessDeeplink(URL, String)
case initializationFailed(String)
case rewindFailed(String)
case cantStartSync(ZcashError)
case cantStoreThatUserPassedPhraseBackupTest(ZcashError)
case failedToProcessDeeplink(URL, ZcashError)
case initializationFailed(ZcashError)
case rewindFailed(ZcashError)
case walletStateFailed(InitializationState)
case wipeFailed
case wipeRequest
}
enum Scan: Equatable {
case cantInitializeCamera(String)
case cantInitializeCamera(ZcashError)
}
enum Settings: Equatable {
case cantBackupWallet(String)
case cantBackupWallet(ZcashError)
case sendSupportMail
}

View File

@ -12,10 +12,10 @@ import ComposableArchitecture
extension AlertRequest {
func balanceBreakdownAlertState(_ balanceBreakdown: BalanceBreakdown) -> AlertState<RootReducer.Action> {
switch balanceBreakdown {
case .shieldFundsFailure(let errorDescription):
case .shieldFundsFailure(let error):
return AlertState(
title: TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Failure.title),
message: TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Failure.message(errorDescription)),
message: TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Failure.message(error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
case .shieldFundsSuccess:
@ -33,10 +33,10 @@ extension AlertRequest {
extension AlertRequest {
func exportLogsAlertState(_ exportLogs: ExportLogs) -> AlertState<RootReducer.Action> {
switch exportLogs {
case .failed(let errorDescription):
case .failed(let error):
return AlertState(
title: TextState(L10n.ExportLogs.Alert.Failed.title),
message: TextState(L10n.ExportLogs.Alert.Failed.message(errorDescription)),
message: TextState(L10n.ExportLogs.Alert.Failed.message(error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
}
@ -48,10 +48,10 @@ extension AlertRequest {
extension AlertRequest {
func homeAlertState(_ home: Home) -> AlertState<RootReducer.Action> {
switch home {
case let .syncFailed(message, secondaryButtonTitle):
case let .syncFailed(error, secondaryButtonTitle):
return AlertState(
title: TextState(L10n.Home.SyncFailed.title),
message: TextState(message),
message: TextState("\(error.message) (code: \(error.code.rawValue))"),
primaryButton: .default(TextState(L10n.Home.SyncFailed.retry), action: .send(.uniAlert(.home(.retrySync)))),
secondaryButton: .default(TextState(secondaryButtonTitle), action: .send(.dismissAlert))
)
@ -70,10 +70,10 @@ extension AlertRequest {
message: TextState(L10n.ImportWallet.Alert.Success.message),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.uniAlert(.importWallet(.successfullyRecovered))))
)
case .failed(let errorDescription):
case .failed(let error):
return AlertState(
title: TextState(L10n.ImportWallet.Alert.Failed.title),
message: TextState(L10n.ImportWallet.Alert.Failed.message(errorDescription)),
message: TextState(L10n.ImportWallet.Alert.Failed.message(error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
}
@ -85,10 +85,10 @@ extension AlertRequest {
extension AlertRequest {
func rootAlertState(_ root: Root) -> AlertState<RootReducer.Action> {
switch root {
case .cantCreateNewWallet(let errorDescription):
case .cantCreateNewWallet(let error):
return AlertState(
title: TextState(L10n.Root.Initialization.Alert.Failed.title),
message: TextState(L10n.Root.Initialization.Alert.CantCreateNewWallet.message(errorDescription)),
message: TextState(L10n.Root.Initialization.Alert.CantCreateNewWallet.message(error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
case .cantLoadSeedPhrase:
@ -97,34 +97,34 @@ extension AlertRequest {
message: TextState(L10n.Root.Initialization.Alert.CantLoadSeedPhrase.message),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
case .cantStartSync(let errorDescription):
case .cantStartSync(let error):
return AlertState(
title: TextState(L10n.Root.Debug.Alert.Rewind.CantStartSync.title),
message: TextState(L10n.Root.Debug.Alert.Rewind.CantStartSync.message(errorDescription)),
message: TextState(L10n.Root.Debug.Alert.Rewind.CantStartSync.message(error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
case .cantStoreThatUserPassedPhraseBackupTest(let errorDescription):
case .cantStoreThatUserPassedPhraseBackupTest(let error):
return AlertState(
title: TextState(L10n.Root.Initialization.Alert.Failed.title),
message: TextState(L10n.Root.Initialization.Alert.CantStoreThatUserPassedPhraseBackupTest.message(errorDescription)),
message: TextState(L10n.Root.Initialization.Alert.CantStoreThatUserPassedPhraseBackupTest.message(error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
case let .failedToProcessDeeplink(url, errorDescription):
case let .failedToProcessDeeplink(url, error):
return AlertState(
title: TextState(L10n.Root.Destination.Alert.FailedToProcessDeeplink.title),
message: TextState(L10n.Root.Destination.Alert.FailedToProcessDeeplink.message(url, errorDescription)),
message: TextState(L10n.Root.Destination.Alert.FailedToProcessDeeplink.message(url, error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
case .initializationFailed(let errorDescription):
case .initializationFailed(let error):
return AlertState(
title: TextState(L10n.Root.Initialization.Alert.SdkInitFailed.title),
message: TextState(L10n.Root.Initialization.Alert.Error.message(errorDescription)),
message: TextState(L10n.Root.Initialization.Alert.Error.message(error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
case .rewindFailed(let errorDescription):
case .rewindFailed(let error):
return AlertState(
title: TextState(L10n.Root.Debug.Alert.Rewind.Failed.title),
message: TextState(L10n.Root.Debug.Alert.Rewind.Failed.message(errorDescription)),
message: TextState(L10n.Root.Debug.Alert.Rewind.Failed.message(error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
case .walletStateFailed(let walletState):
@ -157,10 +157,10 @@ extension AlertRequest {
extension AlertRequest {
func scanAlertState(_ scan: Scan) -> AlertState<RootReducer.Action> {
switch scan {
case .cantInitializeCamera(let errorDescription):
case .cantInitializeCamera(let error):
return AlertState(
title: TextState(L10n.Scan.Alert.CantInitializeCamera.title),
message: TextState(L10n.Scan.Alert.CantInitializeCamera.message(errorDescription)),
message: TextState(L10n.Scan.Alert.CantInitializeCamera.message(error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
}
@ -172,10 +172,10 @@ extension AlertRequest {
extension AlertRequest {
func settingsAlertState(_ settings: Settings) -> AlertState<RootReducer.Action> {
switch settings {
case .cantBackupWallet(let message):
case .cantBackupWallet(let error):
return AlertState<RootReducer.Action>(
title: TextState(L10n.Settings.Alert.CantBackupWallet.title),
message: TextState(L10n.Settings.Alert.CantBackupWallet.message(message)),
message: TextState(L10n.Settings.Alert.CantBackupWallet.message(error.message, error.code.rawValue)),
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
)
case .sendSupportMail:

View File

@ -41,7 +41,7 @@ struct BalanceBreakdownReducer: ReducerProtocol {
case onDisappear
case shieldFunds
case shieldFundsSuccess
case shieldFundsFailure(String)
case shieldFundsFailure(ZcashError)
case synchronizerStateChanged(SynchronizerState)
case updateLatestBlock
}
@ -81,7 +81,7 @@ struct BalanceBreakdownReducer: ReducerProtocol {
await send(.shieldFundsSuccess)
} catch {
await send(.shieldFundsFailure(error.localizedDescription))
await send(.shieldFundsFailure(error.toZcashError()))
}
}
@ -89,9 +89,9 @@ struct BalanceBreakdownReducer: ReducerProtocol {
state.shieldingFunds = false
return EffectTask(value: .alert(.balanceBreakdown(.shieldFundsSuccess)))
case let .shieldFundsFailure(errorDescription):
case .shieldFundsFailure(let error):
state.shieldingFunds = false
return EffectTask(value: .alert(.balanceBreakdown(.shieldFundsFailure(errorDescription))))
return EffectTask(value: .alert(.balanceBreakdown(.shieldFundsFailure(error))))
case .synchronizerStateChanged(let latestState):
state.shieldedBalance = latestState.shieldedBalance.redacted

View File

@ -24,7 +24,7 @@ struct ExportLogsReducer: ReducerProtocol {
case alert(AlertRequest)
case start
case finished(URL?)
case failed(String)
case failed(ZcashError)
case shareFinished
}
@ -43,7 +43,7 @@ struct ExportLogsReducer: ReducerProtocol {
let zippedLogsURL = try await logsHandler.exportAndStoreLogs()
await send(.finished(zippedLogsURL))
} catch {
await send(.failed(error.localizedDescription))
await send(.failed(error.toZcashError()))
}
}
@ -55,10 +55,10 @@ struct ExportLogsReducer: ReducerProtocol {
state.isSharingLogs = true
return .none
case let .failed(errorDescription):
case let .failed(error):
state.exportLogsDisabled = false
state.isSharingLogs = false
return EffectTask(value: .alert(.exportLogs(.failed(errorDescription))))
return EffectTask(value: .alert(.exportLogs(.failed(error))))
case .shareFinished:
state.isSharingLogs = false

View File

@ -49,7 +49,7 @@ struct HomeReducer: ReducerProtocol {
}
var isUpToDate: Bool {
if case .synced = synchronizerStatusSnapshot.syncStatus {
if case .upToDate = synchronizerStatusSnapshot.syncStatus {
return true
}
return false
@ -73,12 +73,12 @@ struct HomeReducer: ReducerProtocol {
case reviewRequestFinished
case send(SendFlowReducer.Action)
case settings(SettingsReducer.Action)
case syncFailed(String)
case syncFailed(ZcashError)
case foundTransactions
case synchronizerStateChanged(SynchronizerState)
case walletEvents(WalletEventsFlowReducer.Action)
case updateDestination(HomeReducer.State.Destination?)
case showSynchronizerErrorAlert(SyncStatusSnapshot)
case showSynchronizerErrorAlert(ZcashError)
case retrySync
case updateWalletEvents([WalletEvent])
}
@ -161,10 +161,10 @@ struct HomeReducer: ReducerProtocol {
state.shieldedBalance = latestState.shieldedBalance.redacted
switch snapshot.syncStatus {
case .error:
return EffectTask(value: .showSynchronizerErrorAlert(snapshot))
case .error(let error):
return EffectTask(value: .showSynchronizerErrorAlert(error.toZcashError()))
case .synced:
case .upToDate:
return .fireAndForget { await reviewRequest.syncFinished() }
default:
@ -207,12 +207,12 @@ struct HomeReducer: ReducerProtocol {
do {
try await sdkSynchronizer.start(true)
} catch {
await send(.syncFailed(error.localizedDescription))
await send(.syncFailed(error.toZcashError()))
}
}
case .showSynchronizerErrorAlert(let snapshot):
return EffectTask(value: .alert(.home(.syncFailed(snapshot.message, L10n.Home.SyncFailed.dismiss))))
case .showSynchronizerErrorAlert(let error):
return EffectTask(value: .alert(.home(.syncFailed(error, L10n.Home.SyncFailed.dismiss))))
case .balanceBreakdown(.onDisappear):
state.destination = nil
@ -224,8 +224,8 @@ struct HomeReducer: ReducerProtocol {
case .debugMenuStartup:
return .none
case .syncFailed(let errorMessage):
return EffectTask(value: .alert(.home(.syncFailed(errorMessage, L10n.General.ok))))
case .syncFailed(let error):
return EffectTask(value: .alert(.home(.syncFailed(error, L10n.General.ok))))
case .alert:
return .none

View File

@ -113,7 +113,7 @@ struct ImportWalletReducer: ReducerProtocol {
EffectTask(value: .initializeSDK)
)
} catch {
return EffectTask(value: .alert(.importWallet(.failed(error.localizedDescription))))
return EffectTask(value: .alert(.importWallet(.failed(error.toZcashError()))))
}
case .updateDestination(let destination):

View File

@ -19,13 +19,13 @@ extension RootReducer {
indirect enum DebugAction: Equatable {
case cancelRescan
case cantStartSync(String)
case cantStartSync(ZcashError)
case flagUpdated
case fullRescan
case quickRescan
case rateTheApp
case rescanBlockchain
case rewindDone(String?, RootReducer.Action)
case rewindDone(ZcashError?, RootReducer.Action)
case testCrashReporter // this will crash the app if live.
case updateFlag(FeatureFlag, Bool)
case walletConfigLoaded(WalletConfig)
@ -63,15 +63,15 @@ extension RootReducer {
state.destinationState.destination = .home
return rewind(policy: .birthday, sourceAction: .fullRescan)
case let .debug(.rewindDone(errorDescription, _)):
if let errorDescription {
return EffectTask(value: .alert(.root(.rewindFailed(errorDescription))))
case let .debug(.rewindDone(error, _)):
if let error {
return EffectTask(value: .alert(.root(.rewindFailed(error.toZcashError()))))
} else {
return .run { send in
do {
try await sdkSynchronizer.start(false)
} catch {
await send(.debug(.cantStartSync(error.localizedDescription)))
await send(.debug(.cantStartSync(error.toZcashError())))
}
}
}
@ -93,8 +93,8 @@ extension RootReducer {
case let .debug(.walletConfigLoaded(walletConfig)):
return EffectTask(value: .updateStateAfterConfigUpdate(walletConfig))
case .debug(.cantStartSync(let errorMessage)):
return EffectTask(value: .alert(.root(.cantStartSync(errorMessage))))
case .debug(.cantStartSync(let error)):
return EffectTask(value: .alert(.root(.cantStartSync(error))))
case .debug(.rateTheApp):
return .none
@ -109,7 +109,7 @@ extension RootReducer {
.replaceEmpty(with: Void())
.map { _ in return RootReducer.Action.debug(.rewindDone(nil, .debug(sourceAction))) }
.catch { error in
return Just(RootReducer.Action.debug(.rewindDone(error.localizedDescription, .debug(sourceAction)))).eraseToAnyPublisher()
return Just(RootReducer.Action.debug(.rewindDone(error.toZcashError(), .debug(sourceAction)))).eraseToAnyPublisher()
}
.receive(on: mainQueue)
.eraseToEffect()

View File

@ -39,7 +39,7 @@ extension RootReducer {
case deeplink(URL)
case deeplinkHome
case deeplinkSend(Zatoshi, String, String)
case deeplinkFailed(URL, String)
case deeplinkFailed(URL, ZcashError)
case updateDestination(RootReducer.DestinationState.Destination)
}
@ -78,7 +78,7 @@ extension RootReducer {
let synchronizerStatus = sdkSynchronizer.latestState().syncStatus
// process the deeplink only if app is initialized and synchronizer synced
guard state.appInitializationState == .initialized && synchronizerStatus == .synced else {
guard state.appInitializationState == .initialized && synchronizerStatus == .upToDate else {
// TODO: [#370] There are many different states and edge cases we need to handle here
// (https://github.com/zcash/secant-ios-wallet/issues/370)
return .none
@ -93,7 +93,7 @@ extension RootReducer {
)
)
} catch {
await send(.destination(.deeplinkFailed(url, error.localizedDescription)))
await send(.destination(.deeplinkFailed(url, error.toZcashError())))
}
}
@ -110,8 +110,8 @@ extension RootReducer {
state.homeState.sendState.memoState.text = memo.redacted
return .none
case let .destination(.deeplinkFailed(url, errorDescription)):
return EffectTask(value: .alert(.root(.failedToProcessDeeplink(url, errorDescription))))
case let .destination(.deeplinkFailed(url, error)):
return EffectTask(value: .alert(.root(.failedToProcessDeeplink(url, error))))
case .home(.walletEvents(.replyTo(let address))):
guard let url = URL(string: "zcash:\(address)") else {

View File

@ -7,6 +7,7 @@
import ComposableArchitecture
import Foundation
import ZcashLightClientKit
/// In this file is a collection of helpers that control all state and action related operations
/// for the `RootReducer` with a connection to the app/wallet initialization and erasure of the wallet.
@ -20,7 +21,7 @@ extension RootReducer {
case checkWalletConfig
case initializeSDK
case initialSetups
case initializationFailed(String)
case initializationFailed(ZcashError)
case nukeWallet
case nukeWalletRequest
case respondToWalletInitializationState(InitializationState)
@ -126,11 +127,11 @@ extension RootReducer {
try await sdkSynchronizer.prepareWith(seedBytes, viewingKey, birthday)
try await sdkSynchronizer.start(false)
} catch {
await send(.initialization(.initializationFailed(error.localizedDescription)))
await send(.initialization(.initializationFailed(error.toZcashError())))
}
}
} catch {
return EffectTask(value: .initialization(.initializationFailed(error.localizedDescription)))
return EffectTask(value: .initialization(.initializationFailed(error.toZcashError())))
}
case .initialization(.checkBackupPhraseValidation):
@ -177,7 +178,7 @@ extension RootReducer {
EffectTask(value: .phraseValidation(.displayBackedUpPhrase))
)
} catch {
return EffectTask(value: .alert(.root(.cantCreateNewWallet(error.localizedDescription))))
return EffectTask(value: .alert(.root(.cantCreateNewWallet(error.toZcashError()))))
}
case .phraseValidation(.succeed):
@ -185,7 +186,7 @@ extension RootReducer {
try walletStorage.markUserPassedPhraseBackupTest(true)
return .none
} catch {
return EffectTask(value: .alert(.root(.cantStoreThatUserPassedPhraseBackupTest(error.localizedDescription))))
return EffectTask(value: .alert(.root(.cantStoreThatUserPassedPhraseBackupTest(error.toZcashError()))))
}
case .initialization(.nukeWalletRequest):
@ -252,9 +253,9 @@ extension RootReducer {
state.homeState.walletConfig = walletConfig
return .none
case .initialization(.initializationFailed(let errorMessage)):
case .initialization(.initializationFailed(let error)):
state.appInitializationState = .failed
return EffectTask(value: .alert(.root(.initializationFailed(errorMessage))))
return EffectTask(value: .alert(.root(.initializationFailed(error))))
case .home, .destination, .onboarding, .phraseDisplay, .phraseValidation, .sandbox,
.welcome, .binding, .debug, .exportLogs, .uniAlert, .dismissAlert, .alert:

View File

@ -70,7 +70,7 @@ struct ScanReducer: ReducerProtocol {
state.isTorchAvailable = try captureDevice.isTorchAvailable()
return .none
} catch {
return EffectTask(value: .alert(.scan(.cantInitializeCamera(error.localizedDescription))))
return EffectTask(value: .alert(.scan(.cantInitializeCamera(error.toZcashError()))))
}
case .onDisappear:
@ -110,7 +110,7 @@ struct ScanReducer: ReducerProtocol {
state.isTorchOn.toggle()
return .none
} catch {
return EffectTask(value: .alert(.scan(.cantInitializeCamera(error.localizedDescription))))
return EffectTask(value: .alert(.scan(.cantInitializeCamera(error.toZcashError()))))
}
}
}

View File

@ -87,7 +87,7 @@ struct SendFlowReducer: ReducerProtocol {
case scan(ScanReducer.Action)
case sendPressed
case sendTransactionSuccess
case sendTransactionFailure(String)
case sendTransactionFailure(ZcashError)
case synchronizerStateChanged(SynchronizerState)
case transactionAddressInput(TransactionAddressTextFieldReducer.Action)
case transactionAmountInput(TransactionAmountTextFieldReducer.Action)
@ -175,7 +175,7 @@ struct SendFlowReducer: ReducerProtocol {
_ = try await sdkSynchronizer.sendTransaction(spendingKey, state.amount, recipient, memo)
await send(SendFlowReducer.Action.sendTransactionSuccess)
} catch {
await send(SendFlowReducer.Action.sendTransactionFailure(error.localizedDescription))
await send(SendFlowReducer.Action.sendTransactionFailure(error.toZcashError()))
}
}
} catch {

View File

@ -66,7 +66,7 @@ struct SettingsReducer: ReducerProtocol {
state.phraseDisplayState.phrase = recoveryPhrase
return EffectTask(value: .updateDestination(.backupPhrase))
} catch {
return EffectTask(value: .alert(.settings(.cantBackupWallet(error.localizedDescription))))
return EffectTask(value: .alert(.settings(.cantBackupWallet(error.toZcashError()))))
}
case .binding(\.$isCrashReportingOn):

View File

@ -56,7 +56,7 @@ struct WalletEventsFlowReducer: ReducerProtocol {
case .onDisappear:
return .cancel(id: CancelId.self)
case .synchronizerStateChanged(.synced):
case .synchronizerStateChanged(.upToDate):
state.latestMinedHeight = sdkSynchronizer.latestScannedHeight()
return .task {
return .updateWalletEvents(try await sdkSynchronizer.getAllTransactions())

View File

@ -19,29 +19,17 @@ struct SyncStatusSnapshot: Equatable {
static func snapshotFor(state: SyncStatus) -> SyncStatusSnapshot {
switch state {
case .enhancing:
return SyncStatusSnapshot(state, L10n.Sync.Message.enhancing)
case .fetching:
return SyncStatusSnapshot(state, L10n.Sync.Message.fetchingUTXO)
case .disconnected:
return SyncStatusSnapshot(state, L10n.Sync.Message.disconnected)
case .stopped:
return SyncStatusSnapshot(state, L10n.Sync.Message.stopped)
case .synced:
case .upToDate:
return SyncStatusSnapshot(state, L10n.Sync.Message.uptodate)
case .unprepared:
return SyncStatusSnapshot(state, L10n.Sync.Message.unprepared)
case .error(let err):
return SyncStatusSnapshot(state, L10n.Sync.Message.error(err.localizedDescription))
case .error(let error):
return SyncStatusSnapshot(state, L10n.Sync.Message.error(error.toZcashError().message))
case .syncing(let progress):
return SyncStatusSnapshot(state, L10n.Sync.Message.sync(String(format: "%0.1f", progress.progress * 100)))
return SyncStatusSnapshot(state, L10n.Sync.Message.sync(String(format: "%0.1f", progress * 100)))
}
}
}

View File

@ -64,9 +64,9 @@ internal enum L10n {
internal enum Alert {
internal enum ShieldFunds {
internal enum Failure {
/// Error: %@
internal static func message(_ p1: Any) -> String {
return L10n.tr("Localizable", "balanceBreakdown.alert.shieldFunds.failure.message", String(describing: p1), fallback: "Error: %@")
/// Error: %@ (code: %@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "balanceBreakdown.alert.shieldFunds.failure.message", String(describing: p1), String(describing: p2), fallback: "Error: %@ (code: %@)")
}
/// Failed to shield funds
internal static let title = L10n.tr("Localizable", "balanceBreakdown.alert.shieldFunds.failure.title", fallback: "Failed to shield funds")
@ -87,9 +87,9 @@ internal enum L10n {
internal enum ExportLogs {
internal enum Alert {
internal enum Failed {
/// Error: %@
internal static func message(_ p1: Any) -> String {
return L10n.tr("Localizable", "exportLogs.alert.failed.message", String(describing: p1), fallback: "Error: %@")
/// Error: %@ (code: %@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "exportLogs.alert.failed.message", String(describing: p1), String(describing: p2), fallback: "Error: %@ (code: %@)")
}
/// Error when exporting logs
internal static let title = L10n.tr("Localizable", "exportLogs.alert.failed.title", fallback: "Error when exporting logs")
@ -175,9 +175,9 @@ internal enum L10n {
internal static let title = L10n.tr("Localizable", "importWallet.title", fallback: "Wallet Import")
internal enum Alert {
internal enum Failed {
/// Error: %@
internal static func message(_ p1: Any) -> String {
return L10n.tr("Localizable", "importWallet.alert.failed.message", String(describing: p1), fallback: "Error: %@")
/// Error: %@ (code: %@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "importWallet.alert.failed.message", String(describing: p1), String(describing: p2), fallback: "Error: %@ (code: %@)")
}
/// Failed to restore wallet
internal static let title = L10n.tr("Localizable", "importWallet.alert.failed.title", fallback: "Failed to restore wallet")
@ -321,17 +321,17 @@ internal enum L10n {
internal enum Alert {
internal enum Rewind {
internal enum CantStartSync {
/// Error: %@
internal static func message(_ p1: Any) -> String {
return L10n.tr("Localizable", "root.debug.alert.rewind.cantStartSync.message", String(describing: p1), fallback: "Error: %@")
/// Error: %@ (code: %@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "root.debug.alert.rewind.cantStartSync.message", String(describing: p1), String(describing: p2), fallback: "Error: %@ (code: %@)")
}
/// Can't start sync process after rewind
internal static let title = L10n.tr("Localizable", "root.debug.alert.rewind.cantStartSync.title", fallback: "Can't start sync process after rewind")
}
internal enum Failed {
/// Error: %@
internal static func message(_ p1: Any) -> String {
return L10n.tr("Localizable", "root.debug.alert.rewind.failed.message", String(describing: p1), fallback: "Error: %@")
/// Error: %@ (code: %@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "root.debug.alert.rewind.failed.message", String(describing: p1), String(describing: p2), fallback: "Error: %@ (code: %@)")
}
/// Rewind failed
internal static let title = L10n.tr("Localizable", "root.debug.alert.rewind.failed.title", fallback: "Rewind failed")
@ -377,9 +377,9 @@ internal enum L10n {
internal enum Alert {
internal enum FailedToProcessDeeplink {
/// Deeplink: (%@))
/// Error: (%@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "root.destination.alert.failedToProcessDeeplink.message", String(describing: p1), String(describing: p2), fallback: "Deeplink: (%@))\nError: (%@)")
/// Error: (%@) (code: %@)
internal static func message(_ p1: Any, _ p2: Any, _ p3: Any) -> String {
return L10n.tr("Localizable", "root.destination.alert.failedToProcessDeeplink.message", String(describing: p1), String(describing: p2), String(describing: p3), fallback: "Deeplink: (%@))\nError: (%@) (code: %@)")
}
/// Failed to process deeplink.
internal static let title = L10n.tr("Localizable", "root.destination.alert.failedToProcessDeeplink.title", fallback: "Failed to process deeplink.")
@ -389,9 +389,9 @@ internal enum L10n {
internal enum Initialization {
internal enum Alert {
internal enum CantCreateNewWallet {
/// Can't create new wallet. Error: %@
internal static func message(_ p1: Any) -> String {
return L10n.tr("Localizable", "root.initialization.alert.cantCreateNewWallet.message", String(describing: p1), fallback: "Can't create new wallet. Error: %@")
/// Can't create new wallet. Error: %@ (code: %@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "root.initialization.alert.cantCreateNewWallet.message", String(describing: p1), String(describing: p2), fallback: "Can't create new wallet. Error: %@ (code: %@)")
}
}
internal enum CantLoadSeedPhrase {
@ -399,15 +399,15 @@ internal enum L10n {
internal static let message = L10n.tr("Localizable", "root.initialization.alert.cantLoadSeedPhrase.message", fallback: "Can't load seed phrase from local storage.")
}
internal enum CantStoreThatUserPassedPhraseBackupTest {
/// Can't store information that user passed phrase backup test. Error: %@
internal static func message(_ p1: Any) -> String {
return L10n.tr("Localizable", "root.initialization.alert.cantStoreThatUserPassedPhraseBackupTest.message", String(describing: p1), fallback: "Can't store information that user passed phrase backup test. Error: %@")
/// Can't store information that user passed phrase backup test. Error: %@ (code: %@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "root.initialization.alert.cantStoreThatUserPassedPhraseBackupTest.message", String(describing: p1), String(describing: p2), fallback: "Can't store information that user passed phrase backup test. Error: %@ (code: %@)")
}
}
internal enum Error {
/// Error: %@
internal static func message(_ p1: Any) -> String {
return L10n.tr("Localizable", "root.initialization.alert.error.message", String(describing: p1), fallback: "Error: %@")
/// Error: %@ (code: %@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "root.initialization.alert.error.message", String(describing: p1), String(describing: p2), fallback: "Error: %@ (code: %@)")
}
}
internal enum Failed {
@ -444,9 +444,9 @@ internal enum L10n {
internal static let scanning = L10n.tr("Localizable", "scan.scanning", fallback: "Scanning...")
internal enum Alert {
internal enum CantInitializeCamera {
/// Error: %@
internal static func message(_ p1: Any) -> String {
return L10n.tr("Localizable", "scan.alert.cantInitializeCamera.message", String(describing: p1), fallback: "Error: %@")
/// Error: %@ (code: %@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "scan.alert.cantInitializeCamera.message", String(describing: p1), String(describing: p2), fallback: "Error: %@ (code: %@)")
}
/// Can't initialize the camera
internal static let title = L10n.tr("Localizable", "scan.alert.cantInitializeCamera.title", fallback: "Can't initialize the camera")
@ -506,9 +506,9 @@ internal enum L10n {
}
internal enum Alert {
internal enum CantBackupWallet {
/// Error: %@
internal static func message(_ p1: Any) -> String {
return L10n.tr("Localizable", "settings.alert.cantBackupWallet.message", String(describing: p1), fallback: "Error: %@")
/// Error: %@ (code: %@)
internal static func message(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "settings.alert.cantBackupWallet.message", String(describing: p1), String(describing: p2), fallback: "Error: %@ (code: %@)")
}
/// Can't backup wallet
internal static let title = L10n.tr("Localizable", "settings.alert.cantBackupWallet.title", fallback: "Can't backup wallet")
@ -565,18 +565,10 @@ internal enum L10n {
}
internal enum Sync {
internal enum Message {
/// disconnected
internal static let disconnected = L10n.tr("Localizable", "sync.message.disconnected", fallback: "disconnected")
/// Enhancing tx
internal static let enhancing = L10n.tr("Localizable", "sync.message.enhancing", fallback: "Enhancing tx")
/// Error: %@
internal static func error(_ p1: Any) -> String {
return L10n.tr("Localizable", "sync.message.error", String(describing: p1), fallback: "Error: %@")
}
/// fetching UTXOs
internal static let fetchingUTXO = L10n.tr("Localizable", "sync.message.fetchingUTXO", fallback: "fetching UTXOs")
/// Stopped
internal static let stopped = L10n.tr("Localizable", "sync.message.stopped", fallback: "Stopped")
/// %@%% Synced
internal static func sync(_ p1: Any) -> String {
return L10n.tr("Localizable", "sync.message.sync", String(describing: p1), fallback: "%@%% Synced")

View File

@ -65,7 +65,7 @@
"importWallet.alert.success.title" = "Success";
"importWallet.alert.success.message" = "The wallet has been successfully recovered.";
"importWallet.alert.failed.title" = "Failed to restore wallet";
"importWallet.alert.failed.message" = "Error: %@";
"importWallet.alert.failed.message" = "Error: %@ (code: %@)";
// MARK: - Home Screen
"home.sendZec" = "Send %@";
@ -96,7 +96,7 @@
"balanceBreakdown.shieldFunds" = "Shield funds";
"balanceBreakdown.shieldingFunds" = "Shielding funds";
"balanceBreakdown.alert.shieldFunds.failure.title" = "Failed to shield funds";
"balanceBreakdown.alert.shieldFunds.failure.message" = "Error: %@";
"balanceBreakdown.alert.shieldFunds.failure.message" = "Error: %@ (code: %@)";
"balanceBreakdown.alert.shieldFunds.success.title" = "Done";
"balanceBreakdown.alert.shieldFunds.success.message" = "Shielding transaction created";
@ -104,7 +104,7 @@
"scan.info" = "We will validate any Zcash URI and take you to the appropriate action.";
"scan.scanning" = "Scanning...";
"scan.alert.cantInitializeCamera.title" = "Can't initialize the camera";
"scan.alert.cantInitializeCamera.message" = "Error: %@";
"scan.alert.cantInitializeCamera.message" = "Error: %@ (code: %@)";
// MARK: - Send
"send.title" = "Send Zcash";
@ -129,15 +129,11 @@
"settings.version" = "Version %@ (%@)";
"settings.title" = "Settings";
"settings.alert.cantBackupWallet.title" = "Can't backup wallet";
"settings.alert.cantBackupWallet.message" = "Error: %@";
"settings.alert.cantBackupWallet.message" = "Error: %@ (code: %@)";
"settings.alert.cantSendEmail.title" = "Can't send email";
"settings.alert.cantSendEmail.message" = "It looks like that you don't have any email account configured on your device. Therefore it's not possible to send a support email.";
// MARK: - Sync message
"sync.message.enhancing" = "Enhancing tx";
"sync.message.fetchingUTXO" = "fetching UTXOs";
"sync.message.disconnected" = "disconnected";
"sync.message.stopped" = "Stopped";
"sync.message.uptodate" = "Up-To-Date";
"sync.message.unprepared" = "Unprepared";
"sync.message.error" = "Error: %@";
@ -219,18 +215,18 @@
// MARK: - Root
"root.initialization.alert.failed.title" = "Wallet initialisation failed.";
"root.initialization.alert.error.message" = "Error: %@";
"root.initialization.alert.error.message" = "Error: %@ (code: %@)";
"root.initialization.alert.sdkInitFailed.title" = "Failed to initialize the SDK";
"root.initialization.alert.walletStateFailed.message" = "App initialisation state: %@.";
"root.initialization.alert.cantLoadSeedPhrase.message" = "Can't load seed phrase from local storage.";
"root.initialization.alert.cantCreateNewWallet.message" = "Can't create new wallet. Error: %@";
"root.initialization.alert.cantStoreThatUserPassedPhraseBackupTest.message" = "Can't store information that user passed phrase backup test. Error: %@";
"root.initialization.alert.cantCreateNewWallet.message" = "Can't create new wallet. Error: %@ (code: %@)";
"root.initialization.alert.cantStoreThatUserPassedPhraseBackupTest.message" = "Can't store information that user passed phrase backup test. Error: %@ (code: %@)";
"root.initialization.alert.wipe.title" = "Wipe of the wallet";
"root.initialization.alert.wipe.message" = "Are you sure?";
"root.initialization.alert.wipeFailed.title" = "Nuke of the wallet failed";
"root.destination.alert.failedToProcessDeeplink.title" = "Failed to process deeplink.";
"root.destination.alert.failedToProcessDeeplink.message" = "Deeplink: \(%@))\nError: \(%@)";
"root.destination.alert.failedToProcessDeeplink.message" = "Deeplink: \(%@))\nError: \(%@) (code: %@)";
"root.debug.title" = "Debug options";
"root.debug.navigationTitle" = "Startup";
@ -249,13 +245,13 @@
"root.debug.dialog.rescan.option.quick" = "Quick rescan";
"root.debug.dialog.rescan.option.full" = "Full rescan";
"root.debug.alert.rewind.failed.title" = "Rewind failed";
"root.debug.alert.rewind.failed.message" = "Error: %@";
"root.debug.alert.rewind.failed.message" = "Error: %@ (code: %@)";
"root.debug.alert.rewind.cantStartSync.title" = "Can't start sync process after rewind";
"root.debug.alert.rewind.cantStartSync.message" = "Error: %@";
"root.debug.alert.rewind.cantStartSync.message" = "Error: %@ (code: %@)";
// MARK: - Export logs
"exportLogs.alert.failed.title" = "Error when exporting logs";
"exportLogs.alert.failed.message" = "Error: %@";
"exportLogs.alert.failed.message" = "Error: %@ (code: %@)";
// MARK: - Text Fields
"field.multiline.charLimitExceeded" = "char limit exceeded";

View File

@ -76,14 +76,14 @@ class BalanceBreakdownTests: XCTestCase {
await store.send(.shieldFunds) { state in
state.shieldingFunds = true
}
await store.receive(.shieldFundsFailure(ZcashError.synchronizerNotPrepared.localizedDescription)) { state in
await store.receive(.shieldFundsFailure(ZcashError.synchronizerNotPrepared)) { state in
state.shieldingFunds = false
}
await store.receive(
.alert(
.balanceBreakdown(
.shieldFundsFailure("The operation couldnt be completed. (ZcashLightClientKit.ZcashError error 140.)")
.shieldFundsFailure(ZcashError.synchronizerNotPrepared)
)
)
)

View File

@ -91,7 +91,7 @@ class DeeplinkTests: XCTestCase {
store.dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(
latestState: {
var state = SynchronizerState.zero
state.syncStatus = .synced
state.syncStatus = .upToDate
return state
}
)
@ -136,7 +136,7 @@ class DeeplinkTests: XCTestCase {
store.dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(
latestState: {
var state = SynchronizerState.zero
state.syncStatus = .synced
state.syncStatus = .upToDate
return state
}
)

View File

@ -14,13 +14,7 @@ import ComposableArchitecture
class HomeTests: XCTestCase {
func testSendButtonIsDisabledWhenSyncing() {
let mockSnapshot = SyncStatusSnapshot.init(
.syncing(
.init(
startHeight: 1_700_000,
targetHeight: 1_800_000,
progressHeight: 1_770_000
)
)
.syncing(0.7)
)
let store = TestStore(
@ -111,12 +105,12 @@ class HomeTests: XCTestCase {
state.synchronizerStatusSnapshot = errorSnapshot
}
store.receive(.showSynchronizerErrorAlert(errorSnapshot))
store.receive(.showSynchronizerErrorAlert(testError))
store.receive(
.alert(
.home(
.syncFailed("Error: The operation couldnt be completed. (ZcashLightClientKit.ZcashError error 140.)", "Dismiss")
.syncFailed(ZcashError.synchronizerNotPrepared, "Dismiss")
)
)
)

View File

@ -36,7 +36,7 @@ final class ReviewRequestTests: XCTestCase {
)
var syncState: SynchronizerState = .zero
syncState.syncStatus = .synced
syncState.syncStatus = .upToDate
let snapshot = SyncStatusSnapshot.snapshotFor(state: syncState.syncStatus)
await store.send(.synchronizerStateChanged(syncState)) { state in

View File

@ -8,6 +8,7 @@
import XCTest
@testable import secant_testnet
import ComposableArchitecture
import ZcashLightClientKit
class RootTests: XCTestCase {
static let testScheduler = DispatchQueue.test
@ -131,6 +132,7 @@ class RootTests: XCTestCase {
func testRespondToWalletInitializationState_FilesMissing() throws {
let walletStorageError: Error = "export failed"
let zcashError = ZcashError.unknown(walletStorageError)
let store = TestStore(
initialState: .placeholder,
@ -151,7 +153,7 @@ class RootTests: XCTestCase {
state.appInitializationState = .failed
}
store.receive(.initialization(.initializationFailed(walletStorageError.localizedDescription)))
store.receive(.initialization(.initializationFailed(zcashError)))
store.receive(.alert(.root(.cantLoadSeedPhrase))) { state in
state.uniAlert = AlertState(
@ -161,10 +163,10 @@ class RootTests: XCTestCase {
)
}
store.receive(.alert(.root(.initializationFailed("The operation couldnt be completed. (Swift.String error 1.)")))) { state in
store.receive(.alert(.root(.initializationFailed(zcashError)))) { state in
state.uniAlert = AlertState(
title: TextState("Failed to initialize the SDK"),
message: TextState("Error: \(walletStorageError.localizedDescription)"),
message: TextState("Error: \(zcashError.message) (code: \(zcashError.code.rawValue))"),
dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert))
)
}
@ -172,6 +174,8 @@ class RootTests: XCTestCase {
func testRespondToWalletInitializationState_Initialized() throws {
let walletStorageError: Error = "export failed"
let zcashError = ZcashError.unknown(walletStorageError)
let store = TestStore(
initialState: .placeholder,
reducer: RootReducer()
@ -189,7 +193,7 @@ class RootTests: XCTestCase {
state.appInitializationState = .failed
}
store.receive(.initialization(.initializationFailed(walletStorageError.localizedDescription)))
store.receive(.initialization(.initializationFailed(zcashError)))
store.receive(.alert(.root(.cantLoadSeedPhrase))) { state in
state.uniAlert = AlertState(
@ -199,10 +203,10 @@ class RootTests: XCTestCase {
)
}
store.receive(.alert(.root(.initializationFailed("The operation couldnt be completed. (Swift.String error 1.)")))) { state in
store.receive(.alert(.root(.initializationFailed(zcashError)))) { state in
state.uniAlert = AlertState(
title: TextState("Failed to initialize the SDK"),
message: TextState("Error: \(walletStorageError.localizedDescription)"),
message: TextState("Error: \(zcashError.message) (code: \(zcashError.code.rawValue))"),
dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert))
)
}

View File

@ -206,7 +206,7 @@ class SendTests: XCTestCase {
// check the failure transaction to be received back
await store.receive(
.sendTransactionFailure("The operation couldnt be completed. (ZcashLightClientKit.ZcashError error 140.)")
.sendTransactionFailure(ZcashError.synchronizerNotPrepared)
) { state in
// from this moment on the sending next transaction is allowed again
// the 'isSendingTransaction' needs to be false again

View File

@ -82,7 +82,7 @@ class WalletEventsTests: XCTestCase {
store.dependencies.mainQueue = .immediate
store.dependencies.sdkSynchronizer = .mocked()
await store.send(.synchronizerStateChanged(.synced)) { state in
await store.send(.synchronizerStateChanged(.upToDate)) { state in
state.latestMinedHeight = 0
}