[#329] Update wallet to use Zatoshi type (#333)

- Int64+Zcash.swift and Double+Zcash.swift removed
- Balances and amounts updated to use Zatoshi
- Remove TODOs
- Tests updated
- FIXED: Send amount being in Zatoshi is clamping $ value to 21M max -> send amount input is no longer Zatoshi typed

[329] Update wallet to use Zatoshi type (333)

- alphabetical order(s)

[329] Update wallet to use Zatoshi type (333)

- static .zero for the Zatoshi
- conformance to Equatable moved to extension

[329] Update wallet to use Zatoshi type (333)

- small improvement by reducing code duplicity
This commit is contained in:
Lukas Korba 2022-05-31 19:14:56 +02:00 committed by GitHub
parent bab1dc6f82
commit b2ae82ce1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 358 additions and 328 deletions

View File

@ -95,14 +95,12 @@
9E2DF99C27CF704D00649636 /* ImportWalletStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */; }; 9E2DF99C27CF704D00649636 /* ImportWalletStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */; };
9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */; }; 9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */; };
9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */; }; 9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */; };
9E2F1C8228095AFE004E65FE /* Int64+Zcash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2F1C8128095AFE004E65FE /* Int64+Zcash.swift */; };
9E2F1C842809B606004E65FE /* DebugMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2F1C832809B606004E65FE /* DebugMenu.swift */; }; 9E2F1C842809B606004E65FE /* DebugMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2F1C832809B606004E65FE /* DebugMenu.swift */; };
9E2F1C8C280ED6A7004E65FE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9E2F1C8B280ED6A7004E65FE /* LaunchScreen.storyboard */; }; 9E2F1C8C280ED6A7004E65FE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9E2F1C8B280ED6A7004E65FE /* LaunchScreen.storyboard */; };
9E2F1C8F280EDE09004E65FE /* Drawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2F1C8E280EDE09004E65FE /* Drawer.swift */; }; 9E2F1C8F280EDE09004E65FE /* Drawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2F1C8E280EDE09004E65FE /* Drawer.swift */; };
9E37A2B827C8F59F00AE57B3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9E37A2B727C8F59F00AE57B3 /* Localizable.strings */; }; 9E37A2B827C8F59F00AE57B3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9E37A2B727C8F59F00AE57B3 /* Localizable.strings */; };
9E391124283E4CAC0073DD9A /* ImportWalletTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E391123283E4CAC0073DD9A /* ImportWalletTests.swift */; }; 9E391124283E4CAC0073DD9A /* ImportWalletTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E391123283E4CAC0073DD9A /* ImportWalletTests.swift */; };
9E391129283F74590073DD9A /* Zatoshi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E391128283F74590073DD9A /* Zatoshi.swift */; }; 9E391129283F74590073DD9A /* Zatoshi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E391128283F74590073DD9A /* Zatoshi.swift */; };
9E39112A283F90F10073DD9A /* Double+Zcash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EDDEA8B28250F9C00B4100C /* Double+Zcash.swift */; };
9E39112E283F91600073DD9A /* ZatoshiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E39112D283F91600073DD9A /* ZatoshiTests.swift */; }; 9E39112E283F91600073DD9A /* ZatoshiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E39112D283F91600073DD9A /* ZatoshiTests.swift */; };
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */; }; 9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */; };
9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; }; 9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; };
@ -287,7 +285,6 @@
9E2DF99827CF704D00649636 /* ImportWalletStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletStore.swift; sourceTree = "<group>"; }; 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletStore.swift; sourceTree = "<group>"; };
9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportSeedEditor.swift; sourceTree = "<group>"; }; 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportSeedEditor.swift; sourceTree = "<group>"; };
9E2DF99B27CF704D00649636 /* ImportWalletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = "<group>"; }; 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = "<group>"; };
9E2F1C8128095AFE004E65FE /* Int64+Zcash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int64+Zcash.swift"; sourceTree = "<group>"; };
9E2F1C832809B606004E65FE /* DebugMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugMenu.swift; sourceTree = "<group>"; }; 9E2F1C832809B606004E65FE /* DebugMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugMenu.swift; sourceTree = "<group>"; };
9E2F1C8B280ED6A7004E65FE /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; }; 9E2F1C8B280ED6A7004E65FE /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
9E2F1C8E280EDE09004E65FE /* Drawer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Drawer.swift; sourceTree = "<group>"; }; 9E2F1C8E280EDE09004E65FE /* Drawer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Drawer.swift; sourceTree = "<group>"; };
@ -330,7 +327,6 @@
9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxStore.swift; sourceTree = "<group>"; }; 9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxStore.swift; sourceTree = "<group>"; };
9EBEF87927CE369800B4F343 /* RecoveryPhraseValidationFlowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseValidationFlowView.swift; sourceTree = "<group>"; }; 9EBEF87927CE369800B4F343 /* RecoveryPhraseValidationFlowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseValidationFlowView.swift; sourceTree = "<group>"; };
9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = "<group>"; }; 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = "<group>"; };
9EDDEA8B28250F9C00B4100C /* Double+Zcash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Zcash.swift"; sourceTree = "<group>"; };
9EDDEA9F2829610D00B4100C /* CurrencySelectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencySelectionTests.swift; sourceTree = "<group>"; }; 9EDDEA9F2829610D00B4100C /* CurrencySelectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencySelectionTests.swift; sourceTree = "<group>"; };
9EDDEAA02829610D00B4100C /* TransactionAmountInputTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAmountInputTests.swift; sourceTree = "<group>"; }; 9EDDEAA02829610D00B4100C /* TransactionAmountInputTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAmountInputTests.swift; sourceTree = "<group>"; };
9EDDEAA12829610D00B4100C /* TransactionAddressInputTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressInputTests.swift; sourceTree = "<group>"; }; 9EDDEAA12829610D00B4100C /* TransactionAddressInputTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressInputTests.swift; sourceTree = "<group>"; };
@ -824,12 +820,10 @@
children = ( children = (
F96B41EA273B50520021B49A /* Strings.swift */, F96B41EA273B50520021B49A /* Strings.swift */,
9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */, 9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */,
9E2F1C8128095AFE004E65FE /* Int64+Zcash.swift */,
0DACFA8027208D940039EEA5 /* UInt+SuperscriptText.swift */, 0DACFA8027208D940039EEA5 /* UInt+SuperscriptText.swift */,
0D7CE63327349B5D0020E050 /* View+WhenDraggable.swift */, 0D7CE63327349B5D0020E050 /* View+WhenDraggable.swift */,
9E7FE0D2282D274E00C374E8 /* Date+Readable.swift */, 9E7FE0D2282D274E00C374E8 /* Date+Readable.swift */,
9E7FE0CE282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift */, 9E7FE0CE282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift */,
9EDDEA8B28250F9C00B4100C /* Double+Zcash.swift */,
0DACFA7E27208CE00039EEA5 /* Clamped.swift */, 0DACFA7E27208CE00039EEA5 /* Clamped.swift */,
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */, F9322DBF273B555C00C105B5 /* NavigationLinks.swift */,
F9C165B3274031F600592F76 /* Bindings.swift */, F9C165B3274031F600592F76 /* Bindings.swift */,
@ -1286,7 +1280,6 @@
9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */, 9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */,
9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */, 9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */,
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */, F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */,
9E2F1C8228095AFE004E65FE /* Int64+Zcash.swift in Sources */,
9E02B56A27FED43E005B809B /* WrappedFileManager.swift in Sources */, 9E02B56A27FED43E005B809B /* WrappedFileManager.swift in Sources */,
663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */, 663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */,
0DC487C32772574C00BE6A63 /* RecoveryPhraseBackupSucceededView.swift in Sources */, 0DC487C32772574C00BE6A63 /* RecoveryPhraseBackupSucceededView.swift in Sources */,
@ -1310,7 +1303,6 @@
0DDB6A5127737D4A0012A410 /* RecoveryPhraseBackupFailedView.swift in Sources */, 0DDB6A5127737D4A0012A410 /* RecoveryPhraseBackupFailedView.swift in Sources */,
9E391129283F74590073DD9A /* Zatoshi.swift in Sources */, 9E391129283F74590073DD9A /* Zatoshi.swift in Sources */,
0D6D628B276A528E002FB4CC /* DropDelegate.swift in Sources */, 0D6D628B276A528E002FB4CC /* DropDelegate.swift in Sources */,
9E39112A283F90F10073DD9A /* Double+Zcash.swift in Sources */,
9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */, 9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */,
F9971A5327680DD000A2DB75 /* ProfileStore.swift in Sources */, F9971A5327680DD000A2DB75 /* ProfileStore.swift in Sources */,
669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */, 669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */,

View File

@ -27,9 +27,9 @@ struct HomeState: Equatable {
var sendState: SendFlowState var sendState: SendFlowState
var scanState: ScanState var scanState: ScanState
var synchronizerStatus: String var synchronizerStatus: String
var totalBalance: Int64 var totalBalance: Zatoshi
var transactionHistoryState: TransactionHistoryFlowState var transactionHistoryState: TransactionHistoryFlowState
var verifiedBalance: Int64 var verifiedBalance: Zatoshi
} }
// MARK: Action // MARK: Action
@ -110,8 +110,8 @@ extension HomeReducer {
return Effect(value: .updateSynchronizerStatus) return Effect(value: .updateSynchronizerStatus)
case .updateBalance(let balance): case .updateBalance(let balance):
state.totalBalance = balance.total state.totalBalance = Zatoshi(amount: balance.total)
state.verifiedBalance = balance.verified state.verifiedBalance = Zatoshi(amount: balance.verified)
return .none return .none
case .updateDrawer(let drawerOverlay): case .updateDrawer(let drawerOverlay):
@ -179,11 +179,11 @@ extension HomeReducer {
action: /HomeAction.send, action: /HomeAction.send,
environment: { environment in environment: { environment in
SendFlowEnvironment( SendFlowEnvironment(
mnemonic: environment.mnemonic,
scheduler: environment.scheduler,
walletStorage: environment.walletStorage,
derivationTool: environment.derivationTool, derivationTool: environment.derivationTool,
SDKSynchronizer: environment.SDKSynchronizer mnemonic: environment.mnemonic,
SDKSynchronizer: environment.SDKSynchronizer,
scheduler: environment.scheduler,
walletStorage: environment.walletStorage
) )
} }
) )
@ -282,9 +282,9 @@ extension HomeState {
sendState: .placeholder, sendState: .placeholder,
scanState: .placeholder, scanState: .placeholder,
synchronizerStatus: "", synchronizerStatus: "",
totalBalance: 0, totalBalance: Zatoshi.zero,
transactionHistoryState: .emptyPlaceHolder, transactionHistoryState: .emptyPlaceHolder,
verifiedBalance: 0 verifiedBalance: Zatoshi.zero
) )
} }
} }

View File

@ -18,7 +18,7 @@ struct HomeView: View {
Text("\(viewStore.synchronizerStatus)") Text("\(viewStore.synchronizerStatus)")
.padding(.top, 60) .padding(.top, 60)
Text("balance \(viewStore.totalBalance.asZecString()) ZEC") Text("balance \(viewStore.totalBalance.decimalString()) ZEC")
.accessDebugMenuWithHiddenGesture { .accessDebugMenuWithHiddenGesture {
viewStore.send(.debugMenuStartup) viewStore.send(.debugMenuStartup)
} }

View File

@ -32,11 +32,11 @@ struct SandboxView: View {
) )
.debug(), .debug(),
environment: SendFlowEnvironment( environment: SendFlowEnvironment(
mnemonic: .live,
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live(),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: LiveWrappedSDKSynchronizer() mnemonic: .live,
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live()
) )
) )
) )

View File

@ -23,11 +23,10 @@ struct SendFlowState: Equatable {
case done case done
} }
var route: Route?
var isSendingTransaction = false var isSendingTransaction = false
var memo = "" var memo = ""
var totalBalance: Int64 = 0 var route: Route?
var totalBalance = Zatoshi.zero
var transaction: SendFlowTransaction var transaction: SendFlowTransaction
var transactionAddressInputState: TransactionAddressTextFieldState var transactionAddressInputState: TransactionAddressTextFieldState
var transactionAmountInputState: TransactionAmountTextFieldState var transactionAmountInputState: TransactionAmountTextFieldState
@ -52,8 +51,8 @@ struct SendFlowState: Equatable {
transactionAmountInputState.amount > transactionAmountInputState.maxValue transactionAmountInputState.amount > transactionAmountInputState.maxValue
} }
var totalCurrencyBalance: Int64 { var totalCurrencyBalance: Zatoshi {
(totalBalance.asHumanReadableZecBalance() * transactionAmountInputState.zecPrice).asZec() Zatoshi.from(decimal: totalBalance.decimalValue.decimalValue * transactionAmountInputState.zecPrice)
} }
} }
@ -67,7 +66,7 @@ enum SendFlowAction: Equatable {
case synchronizerStateChanged(WrappedSDKSynchronizerState) case synchronizerStateChanged(WrappedSDKSynchronizerState)
case transactionAddressInput(TransactionAddressTextFieldAction) case transactionAddressInput(TransactionAddressTextFieldAction)
case transactionAmountInput(TransactionAmountTextFieldAction) case transactionAmountInput(TransactionAmountTextFieldAction)
case updateBalance(Int64) case updateBalance(Zatoshi)
case updateMemo(String) case updateMemo(String)
case updateTransaction(SendFlowTransaction) case updateTransaction(SendFlowTransaction)
case updateRoute(SendFlowState.Route?) case updateRoute(SendFlowState.Route?)
@ -76,11 +75,11 @@ enum SendFlowAction: Equatable {
// MARK: - Environment // MARK: - Environment
struct SendFlowEnvironment { struct SendFlowEnvironment {
let derivationTool: WrappedDerivationTool
let mnemonic: WrappedMnemonic let mnemonic: WrappedMnemonic
let SDKSynchronizer: WrappedSDKSynchronizer
let scheduler: AnySchedulerOf<DispatchQueue> let scheduler: AnySchedulerOf<DispatchQueue>
let walletStorage: WrappedWalletStorage let walletStorage: WrappedWalletStorage
let derivationTool: WrappedDerivationTool
let SDKSynchronizer: WrappedSDKSynchronizer
} }
// MARK: - Reducer // MARK: - Reducer
@ -109,7 +108,7 @@ extension SendFlowReducer {
return .none return .none
case .updateRoute(.confirmation): case .updateRoute(.confirmation):
state.transaction.amount = state.transactionAmountInputState.amount state.transaction.amount = Zatoshi(amount: state.transactionAmountInputState.amount)
state.transaction.toAddress = state.transactionAddressInputState.textFieldState.text state.transaction.toAddress = state.transactionAddressInputState.textFieldState.text
return .none return .none
@ -133,7 +132,7 @@ extension SendFlowReducer {
return environment.SDKSynchronizer.sendTransaction( return environment.SDKSynchronizer.sendTransaction(
with: spendingKey, with: spendingKey,
zatoshi: Int64(state.transaction.amount), zatoshi: state.transaction.amount,
to: state.transaction.toAddress, to: state.transaction.toAddress,
memo: state.transaction.memo, memo: state.transaction.memo,
from: 0 from: 0
@ -172,7 +171,7 @@ extension SendFlowReducer {
case .synchronizerStateChanged(.synced): case .synchronizerStateChanged(.synced):
return environment.SDKSynchronizer.getShieldedBalance() return environment.SDKSynchronizer.getShieldedBalance()
.receive(on: environment.scheduler) .receive(on: environment.scheduler)
.map({ $0.total }) .map({ Zatoshi(amount: $0.total) })
.map(SendFlowAction.updateBalance) .map(SendFlowAction.updateBalance)
.eraseToEffect() .eraseToEffect()
@ -181,7 +180,7 @@ extension SendFlowReducer {
case .updateBalance(let balance): case .updateBalance(let balance):
state.totalBalance = balance state.totalBalance = balance
state.transactionAmountInputState.maxValue = balance state.transactionAmountInputState.maxValue = balance.amount
return .none return .none
case .updateMemo(let memo): case .updateMemo(let memo):
@ -287,7 +286,7 @@ extension SendFlowState {
.init( .init(
route: nil, route: nil,
transaction: .init( transaction: .init(
amount: 0, amount: Zatoshi.zero,
memo: "", memo: "",
toAddress: "" toAddress: ""
), ),
@ -309,11 +308,11 @@ extension SendFlowStore {
), ),
reducer: .default, reducer: .default,
environment: SendFlowEnvironment( environment: SendFlowEnvironment(
mnemonic: .live,
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live(),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: LiveWrappedSDKSynchronizer() mnemonic: .live,
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live()
) )
) )
} }

View File

@ -41,11 +41,11 @@ struct SendFLowView_Previews: PreviewProvider {
), ),
reducer: .default, reducer: .default,
environment: SendFlowEnvironment( environment: SendFlowEnvironment(
mnemonic: .live,
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live(),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: LiveWrappedSDKSynchronizer() mnemonic: .live,
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live()
) )
) )
) )

View File

@ -10,8 +10,8 @@ struct CreateTransaction: View {
return WithViewStore(store) { viewStore in return WithViewStore(store) { viewStore in
VStack { VStack {
VStack(spacing: 0) { VStack(spacing: 0) {
Text("Balance \(viewStore.totalBalance.asZecString()) ZEC") Text("Balance \(viewStore.totalBalance.decimalString()) ZEC")
Text("($\(viewStore.totalCurrencyBalance.asZecString()))") Text("($\(viewStore.totalCurrencyBalance.decimalString()))")
.font(.system(size: 13)) .font(.system(size: 13))
.opacity(0.6) .opacity(0.6)
} }
@ -32,9 +32,7 @@ struct CreateTransaction: View {
Spacer() Spacer()
} }
} } else if viewStore.isInsufficientFunds {
if viewStore.isInsufficientFunds {
HStack { HStack {
Text("insufficient funds") Text("insufficient funds")
.foregroundColor(.red) .foregroundColor(.red)

View File

@ -6,7 +6,7 @@ struct TransactionConfirmation: View {
var body: some View { var body: some View {
VStack { VStack {
Text("Send \(viewStore.transaction.amount.asZecString()) ZEC") Text("Send \(viewStore.transaction.amount.decimalString()) ZEC")
.padding() .padding()
Text("To \(viewStore.transaction.toAddress)") Text("To \(viewStore.transaction.toAddress)")

View File

@ -101,7 +101,7 @@ extension TransactionState {
id: "2", id: "2",
status: .paid(success: true), status: .paid(success: true),
subtitle: "", subtitle: "",
zecAmount: 25 zecAmount: Zatoshi(amount: 25)
) )
} }
} }
@ -153,7 +153,7 @@ extension IdentifiedArrayOf where Element == TransactionState {
id: String($0), id: String($0),
status: .paid(success: true), status: .paid(success: true),
subtitle: "", subtitle: "",
zecAmount: 25 zecAmount: Zatoshi(amount: 25)
) )
} }
) )

View File

@ -52,7 +52,7 @@ extension TransactionHistoryFlowView {
Spacer() Spacer()
Text(transaction.status == .received ? "+" : "") Text(transaction.status == .received ? "+" : "")
+ Text("\(transaction.zecAmount.asZecString()) ZEC") + Text("\(transaction.zecAmount.decimalString()) ZEC")
} }
} }
.navigationLink( .navigationLink(

View File

@ -9,7 +9,7 @@ import Foundation
/// Simple model that holds data throughout the `SendFlow` feature /// Simple model that holds data throughout the `SendFlow` feature
struct SendFlowTransaction: Equatable { struct SendFlowTransaction: Equatable {
var amount: Int64 var amount: Zatoshi
var memo: String var memo: String
var toAddress: String var toAddress: String
} }
@ -17,7 +17,7 @@ struct SendFlowTransaction: Equatable {
extension SendFlowTransaction { extension SendFlowTransaction {
static var placeholder: Self { static var placeholder: Self {
.init( .init(
amount: 0, amount: Zatoshi.zero,
memo: "", memo: "",
toAddress: "" toAddress: ""
) )

View File

@ -25,7 +25,7 @@ struct TransactionState: Equatable, Identifiable {
var id: String var id: String
var status: Status var status: Status
var subtitle: String var subtitle: String
var zecAmount: Int64 var zecAmount: Zatoshi
} }
extension TransactionState { extension TransactionState {
@ -36,7 +36,7 @@ extension TransactionState {
status = sent ? .paid(success: confirmedTransaction.minedHeight > 0) : .received status = sent ? .paid(success: confirmedTransaction.minedHeight > 0) : .received
subtitle = "sent" subtitle = "sent"
zAddress = confirmedTransaction.toAddress zAddress = confirmedTransaction.toAddress
zecAmount = (sent ? -Int64(confirmedTransaction.value) : Int64(confirmedTransaction.value)) zecAmount = sent ? Zatoshi(amount: -Int64(confirmedTransaction.value)) : Zatoshi(amount: Int64(confirmedTransaction.value))
if let memo = confirmedTransaction.memo { if let memo = confirmedTransaction.memo {
self.memo = memo.asZcashTransactionMemo() self.memo = memo.asZcashTransactionMemo()
} }
@ -51,7 +51,7 @@ extension TransactionState {
expirationHeight = pendingTransaction.expiryHeight expirationHeight = pendingTransaction.expiryHeight
subtitle = "pending" subtitle = "pending"
zAddress = pendingTransaction.toAddress zAddress = pendingTransaction.toAddress
zecAmount = -Int64(pendingTransaction.value) zecAmount = Zatoshi(amount: -Int64(pendingTransaction.value))
if let memo = pendingTransaction.memo { if let memo = pendingTransaction.memo {
self.memo = memo.asZcashTransactionMemo() self.memo = memo.asZcashTransactionMemo()
} }
@ -64,7 +64,7 @@ extension TransactionState {
extension TransactionState { extension TransactionState {
static func placeholder( static func placeholder(
date: Date, date: Date,
amount: Int64, amount: Zatoshi,
shielded: Bool = true, shielded: Bool = true,
status: Status = .received, status: Status = .received,
subtitle: String = "", subtitle: String = "",
@ -80,14 +80,14 @@ extension TransactionState {
id: uuid, id: uuid,
status: status, status: status,
subtitle: subtitle, subtitle: subtitle,
zecAmount: status == .received ? amount : -amount zecAmount: status == .received ? amount : Zatoshi(amount: -amount.amount)
) )
} }
} }
struct TransactionStateMockHelper { struct TransactionStateMockHelper {
var date: TimeInterval var date: TimeInterval
var amount: Int64 var amount: Zatoshi
var shielded = true var shielded = true
var status: TransactionState.Status = .received var status: TransactionState.Status = .received
var subtitle = "cleared" var subtitle = "cleared"

View File

@ -17,8 +17,8 @@ typealias TransactionAddressTextFieldReducer = Reducer<
typealias TransactionAddressTextFieldStore = Store<TransactionAddressTextFieldState, TransactionAddressTextFieldAction> typealias TransactionAddressTextFieldStore = Store<TransactionAddressTextFieldState, TransactionAddressTextFieldAction>
struct TransactionAddressTextFieldState: Equatable { struct TransactionAddressTextFieldState: Equatable {
var textFieldState: TCATextFieldState
var isValidAddress = false var isValidAddress = false
var textFieldState: TCATextFieldState
} }
enum TransactionAddressTextFieldAction: Equatable { enum TransactionAddressTextFieldAction: Equatable {

View File

@ -59,11 +59,11 @@ struct TransactionAmountTextField_Previews: PreviewProvider {
TransactionAmountTextField( TransactionAmountTextField(
store: TransactionAmountTextFieldStore( store: TransactionAmountTextFieldStore(
initialState: .init( initialState: .init(
currencySelectionState: .init(currencyType: .usd),
textFieldState: .init( textFieldState: .init(
validationType: .floatingPoint, validationType: .floatingPoint,
text: "" text: ""
), )
currencySelectionState: .init(currencyType: .usd)
), ),
reducer: .default, reducer: .default,
environment: .init() environment: .init()

View File

@ -16,29 +16,12 @@ typealias TransactionAmountTextFieldReducer = Reducer<
typealias TransactionAmountTextFieldStore = Store<TransactionAmountTextFieldState, TransactionAmountTextFieldAction> typealias TransactionAmountTextFieldStore = Store<TransactionAmountTextFieldState, TransactionAmountTextFieldAction>
struct TransactionAmountTextFieldState: Equatable { struct TransactionAmountTextFieldState: Equatable {
var textFieldState: TCATextFieldState var amount: Int64 = 0
var currencySelectionState: CurrencySelectionState var currencySelectionState: CurrencySelectionState
var maxValue: Int64 = 0 var maxValue: Int64 = 0
var textFieldState: TCATextFieldState
// TODO: - Get the ZEC price from the SDK, issue 311, https://github.com/zcash/secant-ios-wallet/issues/311 // TODO: - Get the ZEC price from the SDK, issue 311, https://github.com/zcash/secant-ios-wallet/issues/311
var zecPrice = 140.0 var zecPrice = Decimal(140.0)
var amount: Int64 {
switch currencySelectionState.currencyType {
case .zec:
return (textFieldState.text.doubleValue ?? 0.0).asZec()
case .usd:
return ((textFieldState.text.doubleValue ?? 0.0) / zecPrice).asZec()
}
}
var maxCurrencyConvertedValue: Int64 {
switch currencySelectionState.currencyType {
case .zec:
return maxValue
case .usd:
return (maxValue.asHumanReadableZecBalance() * zecPrice).asZec()
}
}
var isMax: Bool { var isMax: Bool {
return amount == maxValue return amount == maxValue
@ -47,9 +30,10 @@ struct TransactionAmountTextFieldState: Equatable {
enum TransactionAmountTextFieldAction: Equatable { enum TransactionAmountTextFieldAction: Equatable {
case clearValue case clearValue
case currencySelection(CurrencySelectionAction)
case setMax case setMax
case textField(TCATextFieldAction) case textField(TCATextFieldAction)
case currencySelection(CurrencySelectionAction) case updateAmount
} }
struct TransactionAmountTextFieldEnvironment: Equatable {} struct TransactionAmountTextFieldEnvironment: Equatable {}
@ -59,43 +43,65 @@ extension TransactionAmountTextFieldReducer {
[ [
textFieldReducer, textFieldReducer,
currencySelectionReducer, currencySelectionReducer,
maxOverride, amountTextFieldReducer
currencyUpdate
] ]
) )
static let maxOverride = TransactionAmountTextFieldReducer { state, action, _ in static let amountTextFieldReducer = TransactionAmountTextFieldReducer { state, action, _ in
switch action { switch action {
case .setMax: case .setMax:
state.textFieldState.text = "\(state.maxCurrencyConvertedValue.asZecString())" let maxValueAsZec = Decimal(state.maxValue) / Decimal(Zatoshi.Constants.oneZecInZatoshi)
let currencyType = state.currencySelectionState.currencyType
let maxCurrencyConvertedValue: NSDecimalNumber = currencyType == .zec ?
NSDecimalNumber(decimal: maxValueAsZec).roundedZec :
NSDecimalNumber(decimal: maxValueAsZec * state.zecPrice).roundedZec
// TODO: these test will be updated with the NumberFormater dependency to handle locale, issue #312 (https://github.com/zcash/secant-ios-wallet/issues/312)
let decimalString = NumberFormatter.zcashNumberFormatter.string(from: maxCurrencyConvertedValue) ?? ""
state.textFieldState.text = "\(decimalString)"
return Effect(value: .updateAmount)
case .clearValue: case .clearValue:
state.textFieldState.text = "" state.textFieldState.text = ""
return .none
default: break case .textField(.set(let amount)):
} return Effect(value: .updateAmount)
case .updateAmount:
// TODO: these test will be updated with the NumberFormater dependency to handle locale, issue #312 (https://github.com/zcash/secant-ios-wallet/issues/312)
guard var number = NumberFormatter.zcashNumberFormatter.number(from: state.textFieldState.text) else {
state.amount = 0
return .none return .none
} }
switch state.currencySelectionState.currencyType {
case .zec:
state.amount = NSDecimalNumber(decimal: number.decimalValue * Decimal(Zatoshi.Constants.oneZecInZatoshi)).roundedZec.int64Value
case .usd:
let decimal = (number.decimalValue / state.zecPrice) * Decimal(Zatoshi.Constants.oneZecInZatoshi)
state.amount = NSDecimalNumber(decimal: decimal).roundedZec.int64Value
}
return .none
static let currencyUpdate = TransactionAmountTextFieldReducer { state, action, _ in
switch action {
case .currencySelection: case .currencySelection:
guard let currentDoubleValue = state.textFieldState.text.doubleValue else { // TODO: these test will be updated with the NumberFormater dependency to handle locale, issue #312 (https://github.com/zcash/secant-ios-wallet/issues/312)
guard let number = NumberFormatter.zcashNumberFormatter.number(from: state.textFieldState.text) else {
state.amount = 0
return .none return .none
} }
let currencyType = state.currencySelectionState.currencyType let currencyType = state.currencySelectionState.currencyType
let newValue = currencyType == .zec ? let newValue = currencyType == .zec ?
currentDoubleValue / state.zecPrice : number.decimalValue / state.zecPrice :
currentDoubleValue * state.zecPrice number.decimalValue * state.zecPrice
state.textFieldState.text = "\(newValue.asZecString())"
default: break // TODO: these test will be updated with the NumberFormater dependency to handle locale, issue #312 (https://github.com/zcash/secant-ios-wallet/issues/312)
let decimalString = NumberFormatter.zcashNumberFormatter.string(from: NSDecimalNumber(decimal: newValue)) ?? ""
state.textFieldState.text = "\(decimalString)"
return Effect(value: .updateAmount)
} }
return .none
} }
private static let textFieldReducer: TransactionAmountTextFieldReducer = TCATextFieldReducer.default.pullback( private static let textFieldReducer: TransactionAmountTextFieldReducer = TCATextFieldReducer.default.pullback(
@ -113,13 +119,13 @@ extension TransactionAmountTextFieldReducer {
extension TransactionAmountTextFieldState { extension TransactionAmountTextFieldState {
static let placeholder = TransactionAmountTextFieldState( static let placeholder = TransactionAmountTextFieldState(
textFieldState: .placeholder, currencySelectionState: CurrencySelectionState(),
currencySelectionState: CurrencySelectionState() textFieldState: .placeholder
) )
static let amount = TransactionAmountTextFieldState( static let amount = TransactionAmountTextFieldState(
textFieldState: .amount, currencySelectionState: CurrencySelectionState(),
currencySelectionState: CurrencySelectionState() textFieldState: .amount
) )
} }

View File

@ -1,19 +0,0 @@
//
// Double+Zcash.swift
// secant-testnet
//
// Created by Lukáš Korba on 06.05.2022.
//
import Foundation
// TODO: Improve with decimals and zatoshi type, issue #272 (https://github.com/zcash/secant-ios-wallet/issues/272)
extension Double {
func asZec() -> Int64 {
return Int64((self * 100_000_000).rounded())
}
func asZecString() -> String {
NumberFormatter.zcashNumberFormatter.string(from: NSNumber(value: self)) ?? ""
}
}

View File

@ -1,19 +0,0 @@
//
// Int64+Zcash.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.04.2022.
//
import Foundation
// TODO: Improve with decimals and zatoshi type, issue #272 (https://github.com/zcash/secant-ios-wallet/issues/272)
extension Int64 {
func asHumanReadableZecBalance() -> Double {
Double(self) / Double(100_000_000)
}
func asZecString() -> String {
NumberFormatter.zcashNumberFormatter.string(from: NSNumber(value: self.asHumanReadableZecBalance())) ?? ""
}
}

View File

@ -14,6 +14,8 @@ struct Zatoshi {
static let maxZatoshi: Int64 = Constants.oneZecInZatoshi * Constants.maxZecSupply static let maxZatoshi: Int64 = Constants.oneZecInZatoshi * Constants.maxZecSupply
} }
static var zero: Zatoshi { Zatoshi() }
static var decimalHandler = NSDecimalNumberHandler( static var decimalHandler = NSDecimalNumberHandler(
roundingMode: NSDecimalNumber.RoundingMode.bankers, roundingMode: NSDecimalNumber.RoundingMode.bankers,
scale: 8, scale: 8,
@ -32,8 +34,8 @@ struct Zatoshi {
} }
/// Converts `Zatoshi` to human readable format, up to 8 fraction digits /// Converts `Zatoshi` to human readable format, up to 8 fraction digits
var decimalString: String { func decimalString(formatter: NumberFormatter = NumberFormatter.zcashNumberFormatter) -> String {
decimalValue.roundedZec.stringValue formatter.string(from: decimalValue.roundedZec) ?? ""
} }
/// Converts `Decimal` to `Zatoshi` /// Converts `Decimal` to `Zatoshi`
@ -61,14 +63,20 @@ struct Zatoshi {
} }
} }
extension NSDecimalNumber { extension Zatoshi: Equatable {
/// Converts `NSDecimalNumber` to human readable format, up to 8 fraction digits static func == (lhs: Zatoshi, rhs: Zatoshi) -> Bool {
var decimalString: String { lhs.amount == rhs.amount
self.rounding(accordingToBehavior: Zatoshi.decimalHandler).stringValue
} }
}
extension NSDecimalNumber {
/// Round the decimal to 8 fraction digits /// Round the decimal to 8 fraction digits
var roundedZec: NSDecimalNumber { var roundedZec: NSDecimalNumber {
self.rounding(accordingToBehavior: Zatoshi.decimalHandler) self.rounding(accordingToBehavior: Zatoshi.decimalHandler)
} }
/// Converts `NSDecimalNumber` to human readable format, up to 8 fraction digits
var decimalString: String {
self.roundedZec.stringValue
}
} }

View File

@ -57,7 +57,7 @@ protocol WrappedSDKSynchronizer {
func sendTransaction( func sendTransaction(
with spendingKey: String, with spendingKey: String,
zatoshi: Int64, zatoshi: Zatoshi,
to recipientAddress: String, to recipientAddress: String,
memo: String?, memo: String?,
from account: Int from account: Int
@ -220,7 +220,7 @@ class LiveWrappedSDKSynchronizer: WrappedSDKSynchronizer {
func sendTransaction( func sendTransaction(
with spendingKey: String, with spendingKey: String,
zatoshi: Int64, zatoshi: Zatoshi,
to recipientAddress: String, to recipientAddress: String,
memo: String?, memo: String?,
from account: Int from account: Int
@ -229,7 +229,7 @@ class LiveWrappedSDKSynchronizer: WrappedSDKSynchronizer {
Future { [weak self] promise in Future { [weak self] promise in
self?.synchronizer?.sendToAddress( self?.synchronizer?.sendToAddress(
spendingKey: spendingKey, spendingKey: spendingKey,
zatoshi: zatoshi, zatoshi: zatoshi.amount,
toAddress: recipientAddress, toAddress: recipientAddress,
memo: memo, memo: memo,
from: account) { result in from: account) { result in
@ -301,11 +301,11 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
func getAllClearedTransactions() -> Effect<[TransactionState], Never> { func getAllClearedTransactions() -> Effect<[TransactionState], Never> {
let mocked: [TransactionStateMockHelper] = [ let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: 1, status: .paid(success: false)), TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(amount: 1), status: .paid(success: false)),
TransactionStateMockHelper(date: 1651039101, amount: 2), TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(amount: 2)),
TransactionStateMockHelper(date: 1651039000, amount: 3, status: .paid(success: true)), TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(amount: 3), status: .paid(success: true)),
TransactionStateMockHelper(date: 1651039505, amount: 4), TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(amount: 4)),
TransactionStateMockHelper(date: 1651039404, amount: 5) TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(amount: 5))
] ]
return Effect( return Effect(
@ -324,10 +324,10 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
func getAllPendingTransactions() -> Effect<[TransactionState], Never> { func getAllPendingTransactions() -> Effect<[TransactionState], Never> {
let mocked: [TransactionStateMockHelper] = [ let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039606, amount: 6, status: .paid(success: false), subtitle: "pending"), TransactionStateMockHelper(date: 1651039606, amount: Zatoshi(amount: 6), status: .paid(success: false), subtitle: "pending"),
TransactionStateMockHelper(date: 1651039303, amount: 7, subtitle: "pending"), TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(amount: 7), subtitle: "pending"),
TransactionStateMockHelper(date: 1651039707, amount: 8, status: .paid(success: true), subtitle: "pending"), TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(amount: 8), status: .paid(success: true), subtitle: "pending"),
TransactionStateMockHelper(date: 1651039808, amount: 9, subtitle: "pending") TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(amount: 9), subtitle: "pending")
] ]
return Effect( return Effect(
@ -360,7 +360,7 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
func sendTransaction( func sendTransaction(
with spendingKey: String, with spendingKey: String,
zatoshi: Int64, zatoshi: Zatoshi,
to recipientAddress: String, to recipientAddress: String,
memo: String?, memo: String?,
from account: Int from account: Int
@ -375,7 +375,7 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
id: "id", id: "id",
status: .paid(success: true), status: .paid(success: true),
subtitle: "sub", subtitle: "sub",
zecAmount: 10 zecAmount: Zatoshi(amount: 10)
) )
return Effect(value: Result.success(transactionState)) return Effect(value: Result.success(transactionState))
@ -408,11 +408,11 @@ class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
func getAllClearedTransactions() -> Effect<[TransactionState], Never> { func getAllClearedTransactions() -> Effect<[TransactionState], Never> {
let mocked: [TransactionStateMockHelper] = [ let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: 1, status: .paid(success: false), uuid: "aa11"), TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(amount: 1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: 2, uuid: "bb22"), TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(amount: 2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: 3, status: .paid(success: true), uuid: "cc33"), TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(amount: 3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: 4, uuid: "dd44"), TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(amount: 4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: 5, uuid: "ee55") TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(amount: 5), uuid: "ee55")
] ]
return Effect( return Effect(
@ -432,10 +432,16 @@ class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
func getAllPendingTransactions() -> Effect<[TransactionState], Never> { func getAllPendingTransactions() -> Effect<[TransactionState], Never> {
let mocked: [TransactionStateMockHelper] = [ let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039606, amount: 6, status: .paid(success: false), subtitle: "pending", uuid: "ff66"), TransactionStateMockHelper(
TransactionStateMockHelper(date: 1651039303, amount: 7, subtitle: "pending", uuid: "gg77"), date: 1651039606,
TransactionStateMockHelper(date: 1651039707, amount: 8, status: .paid(success: true), subtitle: "pending", uuid: "hh88"), amount: Zatoshi(amount: 6),
TransactionStateMockHelper(date: 1651039808, amount: 9, subtitle: "pending", uuid: "ii99") status: .paid(success: false),
subtitle: "pending",
uuid: "ff66"
),
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(amount: 7), subtitle: "pending", uuid: "gg77"),
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(amount: 8), status: .paid(success: true), subtitle: "pending", uuid: "hh88"),
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(amount: 9), subtitle: "pending", uuid: "ii99")
] ]
return Effect( return Effect(
@ -469,7 +475,7 @@ class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
func sendTransaction( func sendTransaction(
with spendingKey: String, with spendingKey: String,
zatoshi: Int64, zatoshi: Zatoshi,
to recipientAddress: String, to recipientAddress: String,
memo: String?, memo: String?,
from account: Int from account: Int

View File

@ -10,8 +10,6 @@ import XCTest
import ComposableArchitecture import ComposableArchitecture
import ZcashLightClientKit import ZcashLightClientKit
// TODO: these tests will be updated with the Zatoshi/Balance representative once done, issue #272 https://github.com/zcash/secant-ios-wallet/issues/272
// TODO: these test will be updated with the NumberFormater dependency to handle locale, issue #312 (https://github.com/zcash/secant-ios-wallet/issues/312) // TODO: these test will be updated with the NumberFormater dependency to handle locale, issue #312 (https://github.com/zcash/secant-ios-wallet/issues/312)
// swiftlint:disable type_body_length // swiftlint:disable type_body_length
@ -32,11 +30,11 @@ class SendTests: XCTestCase {
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: storage),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: MockWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: MockWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: storage)
) )
let store = TestStore( let store = TestStore(
@ -64,7 +62,7 @@ class SendTests: XCTestCase {
id: "id", id: "id",
status: .paid(success: true), status: .paid(success: true),
subtitle: "sub", subtitle: "sub",
zecAmount: 10 zecAmount: Zatoshi(amount: 10)
) )
// check the success transaction to be received back // check the success transaction to be received back
@ -88,11 +86,11 @@ class SendTests: XCTestCase {
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: storage),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: storage)
) )
let store = TestStore( let store = TestStore(
@ -127,11 +125,11 @@ class SendTests: XCTestCase {
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live))
) )
let store = TestStore( let store = TestStore(
@ -169,11 +167,11 @@ class SendTests: XCTestCase {
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live))
) )
let store = TestStore( let store = TestStore(
@ -185,17 +183,19 @@ class SendTests: XCTestCase {
// Checks the computed property `isInvalidAmountFormat` which controls the error message to be shown on the screen // Checks the computed property `isInvalidAmountFormat` which controls the error message to be shown on the screen
// With empty input it must be false // With empty input it must be false
store.send(.transactionAmountInput(.textField(.set("")))) store.send(.transactionAmountInput(.textField(.set(""))))
store.receive(.transactionAmountInput(.updateAmount))
} }
func testInvalidAddressFormatEmptyInput() throws { func testInvalidAddressFormatEmptyInput() throws {
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live))
) )
let store = TestStore( let store = TestStore(
@ -226,20 +226,20 @@ class SendTests: XCTestCase {
transactionAddressInputState: .placeholder, transactionAddressInputState: .placeholder,
transactionAmountInputState: transactionAmountInputState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
textFieldState: .amount,
currencySelectionState: CurrencySelectionState(), currencySelectionState: CurrencySelectionState(),
maxValue: 501_300 maxValue: 501_300,
textFieldState: .amount
) )
) )
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live))
) )
let store = TestStore( let store = TestStore(
@ -257,9 +257,21 @@ class SendTests: XCTestCase {
) )
} }
store.receive(.transactionAmountInput(.updateAmount)) { state in
state.transactionAmountInputState.amount = 501_299
}
store.send(.transactionAmountInput(.textField(.set("0.00501301")))) { state in store.send(.transactionAmountInput(.textField(.set("0.00501301")))) { state in
state.transactionAmountInputState.textFieldState.text = "0.00501301" state.transactionAmountInputState.textFieldState.text = "0.00501301"
state.transactionAmountInputState.textFieldState.valid = true state.transactionAmountInputState.textFieldState.valid = true
XCTAssertFalse(
state.isInsufficientFunds,
"Send Tests: `testFundsSufficiency` is expected to be false but it's \(state.isInsufficientFunds)"
)
}
store.receive(.transactionAmountInput(.updateAmount)) { state in
state.transactionAmountInputState.amount = 501_301
XCTAssertTrue( XCTAssertTrue(
state.isInsufficientFunds, state.isInsufficientFunds,
"Send Tests: `testFundsSufficiency` is expected to be true but it's \(state.isInsufficientFunds)" "Send Tests: `testFundsSufficiency` is expected to be true but it's \(state.isInsufficientFunds)"
@ -273,11 +285,11 @@ class SendTests: XCTestCase {
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live))
) )
let store = TestStore( let store = TestStore(
@ -286,18 +298,18 @@ class SendTests: XCTestCase {
environment: testEnvironment environment: testEnvironment
) )
try amountFormatTest("1.234", true, 123_400_000, store) try amountFormatTest("1.234", true, 123_400_000, false, store)
try amountFormatTest("1,234", true, 123_400_000_000, store) try amountFormatTest("1,234", true, 123_400_000_000, false, store)
try amountFormatTest("1 234", true, 123_400_000_000, store) try amountFormatTest("1 234", true, 123_400_000_000, true, store)
try amountFormatTest("1,234.567", true, 123_456_700_000, store) try amountFormatTest("1,234.567", true, 123_456_700_000, false, store)
try amountFormatTest("1.", true, 100_000_000, store) try amountFormatTest("1.", true, 100_000_000, false, store)
try amountFormatTest("1..", false, 0, store) try amountFormatTest("1..", false, 0, false, store)
try amountFormatTest("1,.", false, 0, store) try amountFormatTest("1,.", false, 0, true, store)
try amountFormatTest("1.,", false, 0, store) try amountFormatTest("1.,", false, 0, true, store)
try amountFormatTest("1,,", false, 0, store) try amountFormatTest("1,,", false, 0, true, store)
try amountFormatTest("1,23", false, 0, store) try amountFormatTest("1,23", false, 0, true, store)
try amountFormatTest("1 23", false, 0, store) try amountFormatTest("1 23", false, 0, true, store)
try amountFormatTest("1.2.3", false, 0, store) try amountFormatTest("1.2.3", false, 0, true, store)
} }
func testValidForm() throws { func testValidForm() throws {
@ -308,24 +320,25 @@ class SendTests: XCTestCase {
transactionAddressInputState: .placeholder, transactionAddressInputState: .placeholder,
transactionAmountInputState: transactionAmountInputState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
amount: 501_301,
currencySelectionState: CurrencySelectionState(),
maxValue: 501_302,
textFieldState: textFieldState:
TCATextFieldState( TCATextFieldState(
validationType: .floatingPoint, validationType: .floatingPoint,
text: "0.00501301" text: "0.00501301"
), )
currencySelectionState: CurrencySelectionState(),
maxValue: 501_302
) )
) )
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live))
) )
let store = TestStore( let store = TestStore(
@ -353,24 +366,24 @@ class SendTests: XCTestCase {
transactionAddressInputState: .placeholder, transactionAddressInputState: .placeholder,
transactionAmountInputState: transactionAmountInputState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
currencySelectionState: CurrencySelectionState(),
maxValue: 501_300,
textFieldState: textFieldState:
TCATextFieldState( TCATextFieldState(
validationType: .floatingPoint, validationType: .floatingPoint,
text: "0.00501301" text: "0.00501301"
), )
currencySelectionState: CurrencySelectionState(),
maxValue: 501_300
) )
) )
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live))
) )
let store = TestStore( let store = TestStore(
@ -398,24 +411,24 @@ class SendTests: XCTestCase {
transactionAddressInputState: .placeholder, transactionAddressInputState: .placeholder,
transactionAmountInputState: transactionAmountInputState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
currencySelectionState: CurrencySelectionState(),
maxValue: 501_302,
textFieldState: textFieldState:
TCATextFieldState( TCATextFieldState(
validationType: .floatingPoint, validationType: .floatingPoint,
text: "0.00501301" text: "0.00501301"
), )
currencySelectionState: CurrencySelectionState(),
maxValue: 501_302
) )
) )
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live))
) )
let store = TestStore( let store = TestStore(
@ -443,24 +456,24 @@ class SendTests: XCTestCase {
transactionAddressInputState: .placeholder, transactionAddressInputState: .placeholder,
transactionAmountInputState: transactionAmountInputState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
currencySelectionState: CurrencySelectionState(),
maxValue: 501_302,
textFieldState: textFieldState:
TCATextFieldState( TCATextFieldState(
validationType: .floatingPoint, validationType: .floatingPoint,
text: "0.0.0501301" text: "0.0.0501301"
), )
currencySelectionState: CurrencySelectionState(),
maxValue: 501_302
) )
) )
let testScheduler = DispatchQueue.test let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment( let testEnvironment = SendFlowEnvironment(
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
derivationTool: .live(), derivationTool: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer() mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live))
) )
let store = TestStore( let store = TestStore(
@ -488,16 +501,20 @@ private extension SendTests {
_ amount: String, _ amount: String,
_ expectedValidationResult: Bool, _ expectedValidationResult: Bool,
_ expectedAmount: Int64, _ expectedAmount: Int64,
_ expectedToReceive: Bool,
_ store: TestStore<SendFlowState, SendFlowState, SendFlowAction, SendFlowAction, SendFlowEnvironment> _ store: TestStore<SendFlowState, SendFlowState, SendFlowAction, SendFlowAction, SendFlowEnvironment>
) throws { ) throws {
store.send(.transactionAmountInput(.textField(.set(amount)))) { state in store.send(.transactionAmountInput(.textField(.set(amount)))) { state in
state.transactionAmountInputState.textFieldState.text = amount state.transactionAmountInputState.textFieldState.text = amount
state.transactionAmountInputState.textFieldState.valid = expectedValidationResult state.transactionAmountInputState.textFieldState.valid = expectedValidationResult
XCTAssertEqual( }
expectedAmount,
state.transactionAmountInputState.amount, if expectedToReceive {
"Send Tests: `amountFormatTest` expected amount is \(expectedAmount) but result is \(state.isInvalidAddressFormat)" store.receive(.transactionAmountInput(.updateAmount))
) } else {
store.receive(.transactionAmountInput(.updateAmount)) { state in
state.transactionAmountInputState.amount = expectedAmount
}
} }
} }
} }

View File

@ -9,8 +9,6 @@ import XCTest
@testable import secant_testnet @testable import secant_testnet
import ComposableArchitecture import ComposableArchitecture
// TODO: these tests will be updated with the Zatoshi/Balance representative once done, issue #272 https://github.com/zcash/secant-ios-wallet/issues/272
// TODO: these test will be updated with the NumberFormater dependency to handle locale, issue #312 (https://github.com/zcash/secant-ios-wallet/issues/312) // TODO: these test will be updated with the NumberFormater dependency to handle locale, issue #312 (https://github.com/zcash/secant-ios-wallet/issues/312)
class TransactionAmountTextFieldTests: XCTestCase { class TransactionAmountTextFieldTests: XCTestCase {
@ -20,13 +18,13 @@ class TransactionAmountTextFieldTests: XCTestCase {
let store = TestStore( let store = TestStore(
initialState: initialState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
currencySelectionState: CurrencySelectionState(),
maxValue: 501_301,
textFieldState: textFieldState:
TCATextFieldState( TCATextFieldState(
validationType: .floatingPoint, validationType: .floatingPoint,
text: "0.002" text: "0.002"
), )
currencySelectionState: CurrencySelectionState(),
maxValue: 501_301
), ),
reducer: TransactionAmountTextFieldReducer.default, reducer: TransactionAmountTextFieldReducer.default,
environment: TransactionAmountTextFieldEnvironment() environment: TransactionAmountTextFieldEnvironment()
@ -34,7 +32,10 @@ class TransactionAmountTextFieldTests: XCTestCase {
store.send(.setMax) { state in store.send(.setMax) { state in
state.textFieldState.text = "0.00501301" state.textFieldState.text = "0.00501301"
XCTAssertEqual(501_301, state.amount, "AmountInput Tests: `testMaxValue` expected \(501_301) but received \(state.amount)") }
store.receive(.updateAmount) { state in
state.amount = 501_301
} }
} }
@ -42,13 +43,13 @@ class TransactionAmountTextFieldTests: XCTestCase {
let store = TestStore( let store = TestStore(
initialState: initialState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
currencySelectionState: CurrencySelectionState(),
maxValue: 501_301,
textFieldState: textFieldState:
TCATextFieldState( TCATextFieldState(
validationType: .floatingPoint, validationType: .floatingPoint,
text: "0.002" text: "0.002"
), )
currencySelectionState: CurrencySelectionState(),
maxValue: 501_301
), ),
reducer: TransactionAmountTextFieldReducer.default, reducer: TransactionAmountTextFieldReducer.default,
environment: TransactionAmountTextFieldEnvironment() environment: TransactionAmountTextFieldEnvironment()
@ -60,21 +61,21 @@ class TransactionAmountTextFieldTests: XCTestCase {
} }
} }
func testZecUsdConvertedAmount() throws { func testZec_to_UsdConvertedAmount() throws {
try XCTSkipUnless(Locale.current.regionCode == "US", "testZecUsdConvertedAmount is designed to test US locale only") try XCTSkipUnless(Locale.current.regionCode == "US", "testZec_to_UsdConvertedAmount is designed to test US locale only")
let store = TestStore( let store = TestStore(
initialState: initialState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
currencySelectionState:
CurrencySelectionState(
currencyType: .zec
),
textFieldState: textFieldState:
TCATextFieldState( TCATextFieldState(
validationType: .floatingPoint, validationType: .floatingPoint,
text: "1.0" text: "1.0"
), ),
currencySelectionState:
CurrencySelectionState(
currencyType: .zec
),
zecPrice: 1000.0 zecPrice: 1000.0
), ),
reducer: TransactionAmountTextFieldReducer.default, reducer: TransactionAmountTextFieldReducer.default,
@ -84,29 +85,59 @@ class TransactionAmountTextFieldTests: XCTestCase {
store.send(.currencySelection(.swapCurrencyType)) { state in store.send(.currencySelection(.swapCurrencyType)) { state in
state.textFieldState.text = "1,000" state.textFieldState.text = "1,000"
state.currencySelectionState.currencyType = .usd state.currencySelectionState.currencyType = .usd
XCTAssertEqual( }
100_000_000,
state.amount, store.receive(.updateAmount) { state in
"AmountInput Tests: `testZecUsdConvertedAmount` expected \(100_000_000) but received \(state.amount)" state.amount = 100_000_000
)
} }
} }
func testUsdZecConvertedAmount() throws { func testBigZecAmount_to_UsdConvertedAmount() throws {
try XCTSkipUnless(Locale.current.regionCode == "US", "testUsdZecConvertedAmount is designed to test US locale only") try XCTSkipUnless(Locale.current.regionCode == "US", "testBigZecAmount_to_UsdConvertedAmount is designed to test US locale only")
let store = TestStore( let store = TestStore(
initialState: initialState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
currencySelectionState:
CurrencySelectionState(
currencyType: .zec
),
textFieldState:
TCATextFieldState(
validationType: .floatingPoint,
text: "25000"
),
zecPrice: 1000.0
),
reducer: TransactionAmountTextFieldReducer.default,
environment: TransactionAmountTextFieldEnvironment()
)
store.send(.currencySelection(.swapCurrencyType)) { state in
state.textFieldState.text = "25,000,000"
state.currencySelectionState.currencyType = .usd
}
store.receive(.updateAmount) { state in
state.amount = 2_500_000_000_000
}
}
func testUsd_to_ZecConvertedAmount() throws {
try XCTSkipUnless(Locale.current.regionCode == "US", "testUsd_to_ZecConvertedAmount is designed to test US locale only")
let store = TestStore(
initialState:
TransactionAmountTextFieldState(
currencySelectionState:
CurrencySelectionState(
currencyType: .usd
),
textFieldState: textFieldState:
TCATextFieldState( TCATextFieldState(
validationType: .floatingPoint, validationType: .floatingPoint,
text: "1 000" text: "1 000"
), ),
currencySelectionState:
CurrencySelectionState(
currencyType: .usd
),
zecPrice: 1000.0 zecPrice: 1000.0
), ),
reducer: TransactionAmountTextFieldReducer.default, reducer: TransactionAmountTextFieldReducer.default,
@ -116,11 +147,10 @@ class TransactionAmountTextFieldTests: XCTestCase {
store.send(.currencySelection(.swapCurrencyType)) { state in store.send(.currencySelection(.swapCurrencyType)) { state in
state.textFieldState.text = "1" state.textFieldState.text = "1"
state.currencySelectionState.currencyType = .zec state.currencySelectionState.currencyType = .zec
XCTAssertEqual( }
100_000_000,
state.amount, store.receive(.updateAmount) { state in
"AmountInput Tests: `testZecUsdConvertedAmount` expected \(100_000_000) but received \(state.amount)" state.amount = 100_000_000
)
} }
} }
@ -130,16 +160,16 @@ class TransactionAmountTextFieldTests: XCTestCase {
let store = TestStore( let store = TestStore(
initialState: initialState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
textFieldState:
TCATextFieldState(
validationType: .floatingPoint,
text: "5"
),
currencySelectionState: currencySelectionState:
CurrencySelectionState( CurrencySelectionState(
currencyType: .usd currencyType: .usd
), ),
maxValue: 100_000_000, maxValue: 100_000_000,
textFieldState:
TCATextFieldState(
validationType: .floatingPoint,
text: "5"
),
zecPrice: 1000.0 zecPrice: 1000.0
), ),
reducer: TransactionAmountTextFieldReducer.default, reducer: TransactionAmountTextFieldReducer.default,
@ -150,6 +180,14 @@ class TransactionAmountTextFieldTests: XCTestCase {
state.textFieldState.text = "1 000" state.textFieldState.text = "1 000"
state.textFieldState.valid = true state.textFieldState.valid = true
state.currencySelectionState.currencyType = .usd state.currencySelectionState.currencyType = .usd
XCTAssertFalse(
state.isMax,
"AmountInput Tests: `testIfAmountIsMax` is expected to be false but it's \(state.isMax)"
)
}
store.receive(.updateAmount) { state in
state.amount = 100_000_000
XCTAssertTrue( XCTAssertTrue(
state.isMax, state.isMax,
"AmountInput Tests: `testIfAmountIsMax` is expected to be true but it's \(state.isMax)" "AmountInput Tests: `testIfAmountIsMax` is expected to be true but it's \(state.isMax)"
@ -163,16 +201,16 @@ class TransactionAmountTextFieldTests: XCTestCase {
let store = TestStore( let store = TestStore(
initialState: initialState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
textFieldState:
TCATextFieldState(
validationType: .floatingPoint,
text: "5"
),
currencySelectionState: currencySelectionState:
CurrencySelectionState( CurrencySelectionState(
currencyType: .zec currencyType: .zec
), ),
maxValue: 200_000_000, maxValue: 200_000_000,
textFieldState:
TCATextFieldState(
validationType: .floatingPoint,
text: "5"
),
zecPrice: 1000.0 zecPrice: 1000.0
), ),
reducer: TransactionAmountTextFieldReducer.default, reducer: TransactionAmountTextFieldReducer.default,
@ -181,11 +219,10 @@ class TransactionAmountTextFieldTests: XCTestCase {
store.send(.setMax) { state in store.send(.setMax) { state in
state.textFieldState.text = "2" state.textFieldState.text = "2"
XCTAssertEqual( }
200_000_000,
state.maxCurrencyConvertedValue, store.receive(.updateAmount) { state in
"AmountInput Tests: `testMaxZecValue` expected \(200_000_000) but received \(state.maxCurrencyConvertedValue)" state.amount = 200_000_000
)
} }
} }
@ -195,16 +232,16 @@ class TransactionAmountTextFieldTests: XCTestCase {
let store = TestStore( let store = TestStore(
initialState: initialState:
TransactionAmountTextFieldState( TransactionAmountTextFieldState(
textFieldState:
TCATextFieldState(
validationType: .floatingPoint,
text: "5"
),
currencySelectionState: currencySelectionState:
CurrencySelectionState( CurrencySelectionState(
currencyType: .usd currencyType: .usd
), ),
maxValue: 200_000_000, maxValue: 200_000_000,
textFieldState:
TCATextFieldState(
validationType: .floatingPoint,
text: "5"
),
zecPrice: 1000.0 zecPrice: 1000.0
), ),
reducer: TransactionAmountTextFieldReducer.default, reducer: TransactionAmountTextFieldReducer.default,
@ -213,11 +250,10 @@ class TransactionAmountTextFieldTests: XCTestCase {
store.send(.setMax) { state in store.send(.setMax) { state in
state.textFieldState.text = "2,000" state.textFieldState.text = "2,000"
XCTAssertEqual( }
200_000_000_000,
state.maxCurrencyConvertedValue, store.receive(.updateAmount) { state in
"AmountInput Tests: `testMaxUsdValue` expected \(200_000_000_000) but received \(state.maxCurrencyConvertedValue)" state.amount = 200_000_000
)
} }
} }
} }

View File

@ -38,15 +38,21 @@ class TransactionHistoryTests: XCTestCase {
func testSynchronizerStateChanged2Synced() throws { func testSynchronizerStateChanged2Synced() throws {
let mocked: [TransactionStateMockHelper] = [ let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: 1, status: .paid(success: false), uuid: "aa11"), TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(amount: 1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: 2, uuid: "bb22"), TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(amount: 2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: 3, status: .paid(success: true), uuid: "cc33"), TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(amount: 3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: 4, uuid: "dd44"), TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(amount: 4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: 5, uuid: "ee55"), TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(amount: 5), uuid: "ee55"),
TransactionStateMockHelper(date: 1651039606, amount: 6, status: .paid(success: false), subtitle: "pending", uuid: "ff66"), TransactionStateMockHelper(
TransactionStateMockHelper(date: 1651039303, amount: 7, subtitle: "pending", uuid: "gg77"), date: 1651039606,
TransactionStateMockHelper(date: 1651039707, amount: 8, status: .paid(success: true), subtitle: "pending", uuid: "hh88"), amount: Zatoshi(amount: 6),
TransactionStateMockHelper(date: 1651039808, amount: 9, subtitle: "pending", uuid: "ii99") status: .paid(success: false),
subtitle: "pending",
uuid: "ff66"
),
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(amount: 7), subtitle: "pending", uuid: "gg77"),
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(amount: 8), status: .paid(success: true), subtitle: "pending", uuid: "hh88"),
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(amount: 9), subtitle: "pending", uuid: "ii99")
] ]
let transactions = mocked.map { let transactions = mocked.map {

View File

@ -122,9 +122,9 @@ class ZatoshiTests: XCTestCase {
// so we convert it to string, in that case we are prooving it to be rendered // so we convert it to string, in that case we are prooving it to be rendered
// to the user exactly the way we want // to the user exactly the way we want
XCTAssertEqual( XCTAssertEqual(
number.decimalString, number.decimalString(),
"1.42857143", "1.42857143",
"Zatoshi tests: the value is expected to be 1.42857143 but it's \(number.decimalString)" "Zatoshi tests: the value is expected to be 1.42857143 but it's \(number.decimalString())"
) )
} }
@ -151,9 +151,9 @@ class ZatoshiTests: XCTestCase {
func testStringToZatoshi() throws { func testStringToZatoshi() throws {
if let number = Zatoshi.from(decimalString: "200.0") { if let number = Zatoshi.from(decimalString: "200.0") {
XCTAssertEqual( XCTAssertEqual(
number.decimalString, number.decimalString(),
"200", "200",
"Zatoshi tests: `testStringToZec` the value is expected to be 200 but it's \(number.decimalString)" "Zatoshi tests: `testStringToZec` the value is expected to be 200 but it's \(number.decimalString())"
) )
} else { } else {
XCTFail("Zatoshi tests: `testStringToZatoshi` failed to convert number.") XCTFail("Zatoshi tests: `testStringToZatoshi` failed to convert number.")