[#463] Migrate SendFlow to ReducerProtocol (#486)

- SendFlow migrated to ReducerProtocol
- unit and snapshot tests fixed
This commit is contained in:
Lukas Korba 2022-11-08 20:02:55 +01:00 committed by GitHub
parent d758cf4928
commit 1e6397b950
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 280 additions and 501 deletions

View File

@ -13,6 +13,7 @@ typealias AnyBalanceBreakdownReducer = AnyReducer<BalanceBreakdownReducer.State,
typealias AnyScanReducer = AnyReducer<ScanReducer.State, ScanReducer.Action, HomeEnvironment>
typealias AnyWalletEventsFlowReducer = AnyReducer<WalletEventsFlowReducer.State, WalletEventsFlowReducer.Action, HomeEnvironment>
typealias AnyProfileReducer = AnyReducer<ProfileReducer.State, ProfileReducer.Action, HomeEnvironment>
typealias AnySendFlowReducer = AnyReducer<SendFlowReducer.State, SendFlowReducer.Action, HomeEnvironment>
// MARK: State
@ -34,7 +35,7 @@ struct HomeState: Equatable {
var requestState: RequestReducer.State
var requiredTransactionConfirmations = 0
var scanState: ScanReducer.State
var sendState: SendFlowState
var sendState: SendFlowReducer.State
var shieldedBalance: WalletBalance
var synchronizerStatusSnapshot: SyncStatusSnapshot
var walletEventsState: WalletEventsFlowReducer.State
@ -69,7 +70,7 @@ enum HomeAction: Equatable {
case onDisappear
case profile(ProfileReducer.Action)
case request(RequestReducer.Action)
case send(SendFlowAction)
case send(SendFlowReducer.Action)
case scan(ScanReducer.Action)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case walletEvents(WalletEventsFlowReducer.Action)
@ -244,20 +245,13 @@ extension HomeReducer {
environment: { $0 }
)
private static let sendReducer: HomeReducer = SendFlowReducer.default.pullback(
private static let sendReducer: HomeReducer = AnySendFlowReducer { _ in
SendFlowReducer()
}
.pullback(
state: \HomeState.sendState,
action: /HomeAction.send,
environment: { environment in
SendFlowEnvironment(
derivationTool: environment.derivationTool,
mnemonic: environment.mnemonic,
numberFormatter: .live(),
SDKSynchronizer: environment.SDKSynchronizer,
scheduler: environment.scheduler,
walletStorage: environment.walletStorage,
zcashSDKEnvironment: environment.zcashSDKEnvironment
)
}
environment: { $0 }
)
private static let scanReducer: HomeReducer = AnyScanReducer { environment in

View File

@ -27,16 +27,7 @@ struct SandboxView: View {
SendFlowView(
store: .init(
initialState: .placeholder,
reducer: SendFlowReducer.default,
environment: SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .live,
numberFormatter: .live(),
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live(),
zcashSDKEnvironment: .mainnet
)
reducer: SendFlowReducer()
)
)
case .recoveryPhraseDisplay:

View File

@ -9,261 +9,208 @@ import SwiftUI
import ComposableArchitecture
import ZcashLightClientKit
typealias SendFlowReducer = Reducer<SendFlowState, SendFlowAction, SendFlowEnvironment>
typealias SendFlowStore = Store<SendFlowState, SendFlowAction>
typealias SendFlowViewStore = ViewStore<SendFlowState, SendFlowAction>
typealias SendFlowStore = Store<SendFlowReducer.State, SendFlowReducer.Action>
typealias SendFlowViewStore = ViewStore<SendFlowReducer.State, SendFlowReducer.Action>
typealias AnyTransactionAddressTextFieldReducer = AnyReducer<
TransactionAddressTextFieldReducer.State,
TransactionAddressTextFieldReducer.Action,
SendFlowEnvironment
>
typealias AnyTransactionAmountTextFieldReducer = AnyReducer<
TransactionAmountTextFieldReducer.State,
TransactionAmountTextFieldReducer.Action,
SendFlowEnvironment
>
typealias AnyMultiLineTextFieldReducer = AnyReducer<MultiLineTextFieldReducer.State, MultiLineTextFieldReducer.Action, SendFlowEnvironment>
typealias AnyCheckCircleReducer = AnyReducer<Bool, CheckCircleReducer.Action, SendFlowEnvironment>
struct SendFlowReducer: ReducerProtocol {
private enum SyncStatusUpdatesID {}
// MARK: - State
struct State: Equatable {
enum Route: Equatable {
case confirmation
case inProgress
case success
case failure
case done
}
struct SendFlowState: Equatable {
enum Route: Equatable {
case confirmation
case inProgress
case success
case failure
case done
}
var addMemoState: Bool
var isSendingTransaction = false
var memoState: MultiLineTextFieldReducer.State
var route: Route?
var shieldedBalance = WalletBalance.zero
var transactionAddressInputState: TransactionAddressTextFieldReducer.State
var transactionAmountInputState: TransactionAmountTextFieldReducer.State
var addMemoState: Bool
var isSendingTransaction = false
var memoState: MultiLineTextFieldReducer.State
var route: Route?
var shieldedBalance = WalletBalance.zero
var transactionAddressInputState: TransactionAddressTextFieldReducer.State
var transactionAmountInputState: TransactionAmountTextFieldReducer.State
var address: String {
get { transactionAddressInputState.textFieldState.text }
set { transactionAddressInputState.textFieldState.text = newValue }
}
var address: String {
get { transactionAddressInputState.textFieldState.text }
set { transactionAddressInputState.textFieldState.text = newValue }
}
var amount: Zatoshi {
get { Zatoshi(transactionAmountInputState.amount) }
set {
transactionAmountInputState.amount = newValue.amount
transactionAmountInputState.textFieldState.text = newValue.amount == 0 ?
"" :
newValue.decimalString()
}
}
var amount: Zatoshi {
get { Zatoshi(transactionAmountInputState.amount) }
set {
transactionAmountInputState.amount = newValue.amount
transactionAmountInputState.textFieldState.text = newValue.amount == 0 ?
"" :
newValue.decimalString()
var isInvalidAddressFormat: Bool {
!transactionAddressInputState.isValidAddress
&& !transactionAddressInputState.textFieldState.text.isEmpty
}
var isInvalidAmountFormat: Bool {
!transactionAmountInputState.textFieldState.valid
&& !transactionAmountInputState.textFieldState.text.isEmpty
}
var isValidForm: Bool {
transactionAmountInputState.amount > 0
&& transactionAddressInputState.isValidAddress
&& !isInsufficientFunds
&& memoState.isValid
}
var isInsufficientFunds: Bool {
transactionAmountInputState.amount > transactionAmountInputState.maxValue
}
var totalCurrencyBalance: Zatoshi {
Zatoshi.from(decimal: shieldedBalance.total.decimalValue.decimalValue * transactionAmountInputState.zecPrice)
}
}
var isInvalidAddressFormat: Bool {
!transactionAddressInputState.isValidAddress
&& !transactionAddressInputState.textFieldState.text.isEmpty
}
var isInvalidAmountFormat: Bool {
!transactionAmountInputState.textFieldState.valid
&& !transactionAmountInputState.textFieldState.text.isEmpty
enum Action: Equatable {
case addMemo(CheckCircleReducer.Action)
case memo(MultiLineTextFieldReducer.Action)
case onAppear
case onDisappear
case sendConfirmationPressed
case sendTransactionResult(Result<TransactionState, NSError>)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case transactionAddressInput(TransactionAddressTextFieldReducer.Action)
case transactionAmountInput(TransactionAmountTextFieldReducer.Action)
case updateRoute(SendFlowReducer.State.Route?)
}
var isValidForm: Bool {
transactionAmountInputState.amount > 0
&& transactionAddressInputState.isValidAddress
&& !isInsufficientFunds
&& memoState.isValid
}
var isInsufficientFunds: Bool {
transactionAmountInputState.amount > transactionAmountInputState.maxValue
}
@Dependency(\.walletStorage) var walletStorage
@Dependency(\.mnemonic) var mnemonic
@Dependency(\.derivationTool) var derivationTool
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
@Dependency(\.mainQueue) var mainQueue
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
var totalCurrencyBalance: Zatoshi {
Zatoshi.from(decimal: shieldedBalance.total.decimalValue.decimalValue * transactionAmountInputState.zecPrice)
}
}
var body: some ReducerProtocol<State, Action> {
Scope(state: \.addMemoState, action: /Action.addMemo) {
CheckCircleReducer()
}
// MARK: - Action
Scope(state: \.memoState, action: /Action.memo) {
MultiLineTextFieldReducer()
}
enum SendFlowAction: Equatable {
case addMemo(CheckCircleReducer.Action)
case memo(MultiLineTextFieldReducer.Action)
case onAppear
case onDisappear
case sendConfirmationPressed
case sendTransactionResult(Result<TransactionState, NSError>)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case transactionAddressInput(TransactionAddressTextFieldReducer.Action)
case transactionAmountInput(TransactionAmountTextFieldReducer.Action)
case updateRoute(SendFlowState.Route?)
}
Scope(state: \.transactionAddressInputState, action: /Action.transactionAddressInput) {
TransactionAddressTextFieldReducer()
}
// MARK: - Environment
Scope(state: \.transactionAmountInputState, action: /Action.transactionAmountInput) {
TransactionAmountTextFieldReducer()
}
struct SendFlowEnvironment {
let derivationTool: WrappedDerivationTool
let mnemonic: WrappedMnemonic
let numberFormatter: WrappedNumberFormatter
let SDKSynchronizer: WrappedSDKSynchronizer
let scheduler: AnySchedulerOf<DispatchQueue>
let walletStorage: WrappedWalletStorage
let zcashSDKEnvironment: ZCashSDKEnvironment
}
// MARK: - Reducer
extension SendFlowReducer {
private struct SyncStatusUpdatesID: Hashable {}
static let `default` = SendFlowReducer.combine(
[
sendReducer,
transactionAddressInputReducer,
transactionAmountInputReducer,
memoReducer,
addMemoReducer
]
)
private static let sendReducer = SendFlowReducer { state, action, environment in
switch action {
case .addMemo:
return .none
case .updateRoute(.done):
state.route = nil
state.memoState.text = ""
state.transactionAmountInputState.textFieldState.text = ""
state.transactionAmountInputState.amount = 0
state.transactionAddressInputState.textFieldState.text = ""
return .none
case .updateRoute(.failure):
state.route = .failure
state.isSendingTransaction = false
return .none
case .updateRoute(.confirmation):
state.amount = Zatoshi(state.transactionAmountInputState.amount)
state.address = state.transactionAddressInputState.textFieldState.text
state.route = .confirmation
return .none
case let .updateRoute(route):
state.route = route
return .none
case .sendConfirmationPressed:
guard !state.isSendingTransaction else {
Reduce { state, action in
switch action {
case .addMemo:
return .none
}
do {
let storedWallet = try environment.walletStorage.exportWallet()
let seedBytes = try environment.mnemonic.toSeed(storedWallet.seedPhrase)
guard let spendingKey = try environment.derivationTool.deriveSpendingKeys(seedBytes, 1).first else {
return Effect(value: .updateRoute(.failure))
case .updateRoute(.done):
state.route = nil
state.memoState.text = ""
state.transactionAmountInputState.textFieldState.text = ""
state.transactionAmountInputState.amount = 0
state.transactionAddressInputState.textFieldState.text = ""
return .none
case .updateRoute(.failure):
state.route = .failure
state.isSendingTransaction = false
return .none
case .updateRoute(.confirmation):
state.amount = Zatoshi(state.transactionAmountInputState.amount)
state.address = state.transactionAddressInputState.textFieldState.text
state.route = .confirmation
return .none
case let .updateRoute(route):
state.route = route
return .none
case .sendConfirmationPressed:
guard !state.isSendingTransaction else {
return .none
}
state.isSendingTransaction = true
do {
let storedWallet = try walletStorage.exportWallet()
let seedBytes = try mnemonic.toSeed(storedWallet.seedPhrase)
guard let spendingKey = try derivationTool.deriveSpendingKeys(seedBytes, 1).first else {
return Effect(value: .updateRoute(.failure))
}
let sendTransActionEffect = environment.SDKSynchronizer.sendTransaction(
with: spendingKey,
zatoshi: state.amount,
to: state.address,
memo: state.addMemoState ? state.memoState.text : nil,
from: 0
)
.receive(on: environment.scheduler)
.map(SendFlowAction.sendTransactionResult)
.eraseToEffect()
state.isSendingTransaction = true
return .concatenate(
Effect(value: .updateRoute(.inProgress)),
sendTransActionEffect
)
} catch {
return Effect(value: .updateRoute(.failure))
let sendTransActionEffect = sdkSynchronizer.sendTransaction(
with: spendingKey,
zatoshi: state.amount,
to: state.address,
memo: state.addMemoState ? state.memoState.text : nil,
from: 0
)
.receive(on: mainQueue)
.map(SendFlowReducer.Action.sendTransactionResult)
.eraseToEffect()
return .concatenate(
Effect(value: .updateRoute(.inProgress)),
sendTransActionEffect
)
} catch {
return Effect(value: .updateRoute(.failure))
}
case .sendTransactionResult(let result):
state.isSendingTransaction = false
do {
_ = try result.get()
return Effect(value: .updateRoute(.success))
} catch {
return Effect(value: .updateRoute(.failure))
}
case .transactionAmountInput:
return .none
case .transactionAddressInput:
return .none
case .onAppear:
state.memoState.charLimit = zcashSDKEnvironment.memoCharLimit
return sdkSynchronizer.stateChanged
.map(SendFlowReducer.Action.synchronizerStateChanged)
.eraseToEffect()
.cancellable(id: SyncStatusUpdatesID.self, cancelInFlight: true)
case .onDisappear:
return Effect.cancel(id: SyncStatusUpdatesID.self)
case .synchronizerStateChanged(.synced):
if let shieldedBalance = sdkSynchronizer.latestScannedSynchronizerState?.shieldedBalance {
state.shieldedBalance = shieldedBalance
state.transactionAmountInputState.maxValue = shieldedBalance.total.amount
}
return .none
case .synchronizerStateChanged:
return .none
case .memo:
return .none
}
case .sendTransactionResult(let result):
state.isSendingTransaction = false
do {
let transaction = try result.get()
return Effect(value: .updateRoute(.success))
} catch {
return Effect(value: .updateRoute(.failure))
}
case .transactionAmountInput(let transactionInput):
return .none
case .transactionAddressInput(let transactionInput):
return .none
case .onAppear:
state.memoState.charLimit = environment.zcashSDKEnvironment.memoCharLimit
return environment.SDKSynchronizer.stateChanged
.map(SendFlowAction.synchronizerStateChanged)
.eraseToEffect()
.cancellable(id: SyncStatusUpdatesID(), cancelInFlight: true)
case .onDisappear:
return Effect.cancel(id: SyncStatusUpdatesID())
case .synchronizerStateChanged(.synced):
if let shieldedBalance = environment.SDKSynchronizer.latestScannedSynchronizerState?.shieldedBalance {
state.shieldedBalance = shieldedBalance
state.transactionAmountInputState.maxValue = shieldedBalance.total.amount
}
return .none
case .synchronizerStateChanged(let synchronizerState):
return .none
case .memo:
return .none
}
}
private static let addMemoReducer: SendFlowReducer = AnyCheckCircleReducer { _ in
CheckCircleReducer()
}
.pullback(
state: \SendFlowState.addMemoState,
action: /SendFlowAction.addMemo,
environment: { $0 }
)
private static let transactionAddressInputReducer: SendFlowReducer = AnyTransactionAddressTextFieldReducer { _ in
TransactionAddressTextFieldReducer()
}
.pullback(
state: \SendFlowState.transactionAddressInputState,
action: /SendFlowAction.transactionAddressInput,
environment: { $0 }
)
private static let transactionAmountInputReducer: SendFlowReducer = AnyTransactionAmountTextFieldReducer { _ in
TransactionAmountTextFieldReducer()
}
.pullback(
state: \SendFlowState.transactionAmountInputState,
action: /SendFlowAction.transactionAmountInput,
environment: { $0 }
)
private static let memoReducer: SendFlowReducer = AnyMultiLineTextFieldReducer { _ in
MultiLineTextFieldReducer()
}
.pullback(
state: \SendFlowState.memoState,
action: /SendFlowAction.memo,
environment: { $0 }
)
}
// MARK: - Store
@ -272,14 +219,14 @@ extension SendFlowStore {
func addMemoStore() -> CheckCircleStore {
self.scope(
state: \.addMemoState,
action: SendFlowAction.addMemo
action: SendFlowReducer.Action.addMemo
)
}
func memoStore() -> MultiLineTextFieldStore {
self.scope(
state: \.memoState,
action: SendFlowAction.memo
action: SendFlowReducer.Action.memo
)
}
}
@ -287,10 +234,10 @@ extension SendFlowStore {
// MARK: - ViewStore
extension SendFlowViewStore {
var routeBinding: Binding<SendFlowState.Route?> {
var routeBinding: Binding<SendFlowReducer.State.Route?> {
self.binding(
get: \.route,
send: SendFlowAction.updateRoute
send: SendFlowReducer.Action.updateRoute
)
}
@ -302,7 +249,7 @@ extension SendFlowViewStore {
$0 == .success ||
$0 == .failure
},
embed: { $0 ? SendFlowState.Route.confirmation : nil }
embed: { $0 ? SendFlowReducer.State.Route.confirmation : nil }
)
}
@ -313,28 +260,28 @@ extension SendFlowViewStore {
$0 == .success ||
$0 == .failure
},
embed: { $0 ? SendFlowState.Route.inProgress : SendFlowState.Route.confirmation }
embed: { $0 ? SendFlowReducer.State.Route.inProgress : SendFlowReducer.State.Route.confirmation }
)
}
var bindingForSuccess: Binding<Bool> {
self.routeBinding.map(
extract: { $0 == .success },
embed: { _ in SendFlowState.Route.success }
embed: { _ in SendFlowReducer.State.Route.success }
)
}
var bindingForFailure: Binding<Bool> {
self.routeBinding.map(
extract: { $0 == .failure },
embed: { _ in SendFlowState.Route.failure }
embed: { _ in SendFlowReducer.State.Route.failure }
)
}
}
// MARK: Placeholders
extension SendFlowState {
extension SendFlowReducer.State {
static var placeholder: Self {
.init(
addMemoState: true,
@ -361,16 +308,7 @@ extension SendFlowStore {
static var placeholder: SendFlowStore {
return SendFlowStore(
initialState: .emptyPlaceholder,
reducer: .default,
environment: SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .live,
numberFormatter: .live(),
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live(),
zcashSDKEnvironment: .mainnet
)
reducer: SendFlowReducer()
)
}
}

View File

@ -40,16 +40,7 @@ struct SendFLowView_Previews: PreviewProvider {
transactionAddressInputState: .placeholder,
transactionAmountInputState: .placeholder
),
reducer: .default,
environment: SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .live,
numberFormatter: .live(),
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live(),
zcashSDKEnvironment: .mainnet
)
reducer: SendFlowReducer()
)
)
.navigationBarTitleDisplayMode(.inline)

View File

@ -21,7 +21,7 @@ struct CreateTransaction: View {
TransactionAmountTextField(
store: store.scope(
state: \.transactionAmountInputState,
action: SendFlowAction.transactionAmountInput
action: SendFlowReducer.Action.transactionAmountInput
)
)
@ -47,7 +47,7 @@ struct CreateTransaction: View {
TransactionAddressTextField(
store: store.scope(
state: \.transactionAddressInputState,
action: SendFlowAction.transactionAddressInput
action: SendFlowReducer.Action.transactionAddressInput
)
)

View File

@ -34,20 +34,11 @@ class SendTests: XCTestCase {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: MockWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: storage),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: .placeholder,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: storage))
)
// simulate the sending confirmation button to be pressed
@ -97,23 +88,14 @@ class SendTests: XCTestCase {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: MockWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: storage),
zcashSDKEnvironment: .testnet
)
var state = SendFlowState.placeholder
var state = SendFlowReducer.State.placeholder
state.addMemoState = false
let store = TestStore(
initialState: state,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: storage))
)
// simulate the sending confirmation button to be pressed
@ -163,20 +145,12 @@ class SendTests: XCTestCase {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: storage),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: .placeholder,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: storage))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
// simulate the sending confirmation button to be pressed
@ -208,21 +182,13 @@ class SendTests: XCTestCase {
func testAddressValidation() throws {
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: .placeholder,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.send(.transactionAddressInput(.textField(.set("3HRG769ii3HDSJV5vNknQPzXqtL2mTSGnr")))) { state in
@ -252,21 +218,13 @@ class SendTests: XCTestCase {
func testInvalidAmountFormatEmptyInput() throws {
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: .placeholder,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
// Checks the computed property `isInvalidAmountFormat` which controls the error message to be shown on the screen
@ -278,21 +236,13 @@ class SendTests: XCTestCase {
func testInvalidAddressFormatEmptyInput() throws {
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: .placeholder,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
// Checks the computed property `isInvalidAddressFormat` which controls the error message to be shown on the screen
@ -310,7 +260,7 @@ class SendTests: XCTestCase {
}
func testFundsSufficiency() throws {
let sendState = SendFlowState(
let sendState = SendFlowReducer.State(
addMemoState: true,
memoState: .placeholder,
transactionAddressInputState: .placeholder,
@ -327,21 +277,13 @@ class SendTests: XCTestCase {
)
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(numberFormatter: usNumberFormatter),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.send(.transactionAmountInput(.textField(.set("0.00501299")))) { state in
@ -377,16 +319,6 @@ class SendTests: XCTestCase {
func testDifferentAmountFormats() throws {
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(numberFormatter: usNumberFormatter),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: .init(
@ -404,8 +336,10 @@ class SendTests: XCTestCase {
)
)
),
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
try amountFormatTest("1.234", true, 123_400_000, false, store)
@ -423,7 +357,7 @@ class SendTests: XCTestCase {
}
func testValidForm() throws {
let sendState = SendFlowState(
let sendState = SendFlowReducer.State(
addMemoState: true,
memoState: .placeholder,
transactionAddressInputState: .placeholder,
@ -442,20 +376,12 @@ class SendTests: XCTestCase {
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.send(.transactionAddressInput(.textField(.set("t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po")))) { state in
@ -472,7 +398,7 @@ class SendTests: XCTestCase {
}
func testInvalidForm_InsufficientFunds() throws {
let sendState = SendFlowState(
let sendState = SendFlowReducer.State(
addMemoState: true,
memoState: .placeholder,
transactionAddressInputState: .placeholder,
@ -489,21 +415,13 @@ class SendTests: XCTestCase {
)
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.send(.transactionAddressInput(.textField(.set("t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po")))) { state in
@ -520,7 +438,7 @@ class SendTests: XCTestCase {
}
func testInvalidForm_AddressFormat() throws {
let sendState = SendFlowState(
let sendState = SendFlowReducer.State(
addMemoState: true,
memoState: .placeholder,
transactionAddressInputState: .placeholder,
@ -538,20 +456,12 @@ class SendTests: XCTestCase {
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.send(.transactionAddressInput(.textField(.set("3HRG769ii3HDSJV5vNknQPzXqtL2mTSGnr")))) { state in
@ -568,7 +478,7 @@ class SendTests: XCTestCase {
}
func testInvalidForm_AmountFormat() throws {
let sendState = SendFlowState(
let sendState = SendFlowReducer.State(
addMemoState: true,
memoState: .placeholder,
transactionAddressInputState: .placeholder,
@ -586,20 +496,12 @@ class SendTests: XCTestCase {
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.send(.transactionAddressInput(.textField(.set("t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po")))) { state in
@ -616,7 +518,7 @@ class SendTests: XCTestCase {
}
func testInvalidForm_ExceededMemoCharLimit() throws {
let sendState = SendFlowState(
let sendState = SendFlowReducer.State(
addMemoState: true,
memoState: MultiLineTextFieldReducer.State(charLimit: 3),
shieldedBalance: WalletBalance(verified: Zatoshi(1), total: Zatoshi(1)),
@ -644,20 +546,12 @@ class SendTests: XCTestCase {
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.send(.memo(.binding(.set(\.$text, "test")))) { state in
@ -670,7 +564,7 @@ class SendTests: XCTestCase {
}
func testMemoCharLimitSet() throws {
let sendState = SendFlowState(
let sendState = SendFlowReducer.State(
addMemoState: true,
memoState: .placeholder,
transactionAddressInputState: .placeholder,
@ -688,20 +582,12 @@ class SendTests: XCTestCase {
let testScheduler = DispatchQueue.test
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
scheduler: testScheduler.eraseToAnyScheduler(),
walletStorage: .live(walletStorage: WalletStorage(secItem: .live)),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.send(.onAppear) { state in
@ -722,7 +608,7 @@ private extension SendTests {
_ expectedValidationResult: Bool,
_ expectedAmount: Int64,
_ expectedToReceive: Bool,
_ store: TestStore<SendFlowState, SendFlowAction, SendFlowState, SendFlowAction, SendFlowEnvironment>
_ store: TestStore<SendFlowReducer.State, SendFlowReducer.Action, SendFlowReducer.State, SendFlowReducer.Action, Void>
) throws {
store.send(.transactionAmountInput(.textField(.set(amount)))) { state in
state.transactionAmountInputState.textFieldState.text = amount

View File

@ -13,17 +13,7 @@ import ZcashLightClientKit
class TransactionConfirmationSnapshotTests: XCTestCase {
func testTransactionConfirmationSnapshot_addMemo() throws {
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(derivationTool: DerivationTool(networkType: .testnet)),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: MockWrappedSDKSynchronizer(),
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live(),
zcashSDKEnvironment: .testnet
)
var state = SendFlowState.placeholder
var state = SendFlowReducer.State.placeholder
state.addMemoState = true
state.transactionAddressInputState = TransactionAddressTextFieldReducer.State(
textFieldState: TCATextFieldReducer.State(
@ -41,8 +31,11 @@ class TransactionConfirmationSnapshotTests: XCTestCase {
let store = Store(
initialState: state,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.derivationTool, .live(derivationTool: DerivationTool(networkType: .testnet)))
.dependency(\.mainQueue, DispatchQueue.main.eraseToAnyScheduler())
.dependency(\.walletStorage, .live())
.dependency(\.numberFormatter, .live())
)
ViewStore(store).send(.onAppear)
@ -50,17 +43,7 @@ class TransactionConfirmationSnapshotTests: XCTestCase {
}
func testTransactionConfirmationSnapshot_dontAddMemo() throws {
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(derivationTool: DerivationTool(networkType: .testnet)),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: MockWrappedSDKSynchronizer(),
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live(),
zcashSDKEnvironment: .testnet
)
var state = SendFlowState.placeholder
var state = SendFlowReducer.State.placeholder
state.addMemoState = true
state.transactionAddressInputState = TransactionAddressTextFieldReducer.State(
textFieldState: TCATextFieldReducer.State(
@ -78,8 +61,11 @@ class TransactionConfirmationSnapshotTests: XCTestCase {
let store = Store(
initialState: state,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.derivationTool, .live(derivationTool: DerivationTool(networkType: .testnet)))
.dependency(\.mainQueue, DispatchQueue.main.eraseToAnyScheduler())
.dependency(\.walletStorage, .live())
.dependency(\.numberFormatter, .live())
)
ViewStore(store).send(.onAppear)

View File

@ -13,17 +13,7 @@ import ZcashLightClientKit
class TransactionSendingTests: XCTestCase {
func testTransactionSendingSnapshot() throws {
let testEnvironment = SendFlowEnvironment(
derivationTool: .live(derivationTool: DerivationTool(networkType: .testnet)),
mnemonic: .mock,
numberFormatter: .live(),
SDKSynchronizer: MockWrappedSDKSynchronizer(),
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
walletStorage: .live(),
zcashSDKEnvironment: .testnet
)
var state = SendFlowState.placeholder
var state = SendFlowReducer.State.placeholder
state.addMemoState = true
state.transactionAddressInputState = TransactionAddressTextFieldReducer.State(
textFieldState: TCATextFieldReducer.State(
@ -41,8 +31,11 @@ class TransactionSendingTests: XCTestCase {
let store = Store(
initialState: state,
reducer: SendFlowReducer.default,
environment: testEnvironment
reducer: SendFlowReducer()
.dependency(\.derivationTool, .live(derivationTool: DerivationTool(networkType: .testnet)))
.dependency(\.mainQueue, DispatchQueue.main.eraseToAnyScheduler())
.dependency(\.walletStorage, .live())
.dependency(\.numberFormatter, .live())
)
ViewStore(store).send(.onAppear)