2022-05-02 15:35:03 -07:00
|
|
|
//
|
|
|
|
// SendTests.swift
|
|
|
|
// secantTests
|
|
|
|
//
|
|
|
|
// Created by Lukáš Korba on 02.05.2022.
|
|
|
|
//
|
|
|
|
|
|
|
|
import XCTest
|
|
|
|
@testable import secant_testnet
|
|
|
|
import ComposableArchitecture
|
|
|
|
import ZcashLightClientKit
|
|
|
|
|
2022-05-11 08:26:39 -07:00
|
|
|
// swiftlint:disable type_body_length
|
2022-05-02 15:35:03 -07:00
|
|
|
class SendTests: XCTestCase {
|
|
|
|
var storage = WalletStorage(secItem: .live)
|
2022-06-03 05:28:48 -07:00
|
|
|
let usNumberFormatter = NumberFormatter()
|
2022-05-02 15:35:03 -07:00
|
|
|
|
|
|
|
override func setUp() {
|
|
|
|
super.setUp()
|
2022-05-26 05:47:55 -07:00
|
|
|
storage.zcashStoredWalletPrefix = "test_send_"
|
2022-05-02 15:35:03 -07:00
|
|
|
storage.deleteData(forKey: WalletStorage.Constants.zcashStoredWallet)
|
2022-06-03 05:28:48 -07:00
|
|
|
|
|
|
|
usNumberFormatter.maximumFractionDigits = 8
|
|
|
|
usNumberFormatter.maximumIntegerDigits = 8
|
|
|
|
usNumberFormatter.numberStyle = .decimal
|
|
|
|
usNumberFormatter.usesGroupingSeparator = true
|
|
|
|
usNumberFormatter.locale = Locale(identifier: "en_US")
|
2022-05-02 15:35:03 -07:00
|
|
|
}
|
|
|
|
|
2022-11-22 02:32:48 -08:00
|
|
|
@MainActor func testSendSucceeded() async throws {
|
|
|
|
// the test needs to pass the exportWallet() so we simulate some in the keychain
|
|
|
|
try storage.importWallet(bip39: "one two three", birthday: nil)
|
|
|
|
|
2022-05-02 15:35:03 -07:00
|
|
|
// setup the store and environment to be fully mocked
|
|
|
|
let testScheduler = DispatchQueue.test
|
2022-05-11 08:26:39 -07:00
|
|
|
|
2022-11-22 02:32:48 -08:00
|
|
|
var initialState = SendFlowReducer.State.placeholder
|
|
|
|
initialState.transactionAddressInputState = TransactionAddressTextFieldReducer.State(
|
|
|
|
textFieldState:
|
|
|
|
TCATextFieldReducer.State(
|
|
|
|
validationType: nil,
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "ztestsapling1psqa06alcfj9t6s246hht3n7kcw5h900r6z40qnuu7l58qs55kzeqa98879z9hzy596dca4hmsr".redacted
|
2022-11-22 02:32:48 -08:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2022-05-02 15:35:03 -07:00
|
|
|
let store = TestStore(
|
2022-11-22 02:32:48 -08:00
|
|
|
initialState: initialState,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) { dependencies in
|
2022-11-22 02:32:48 -08:00
|
|
|
dependencies.derivationTool = .liveValue
|
2022-11-18 02:34:33 -08:00
|
|
|
dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
|
2022-11-22 02:32:48 -08:00
|
|
|
dependencies.mnemonic = .liveValue
|
2023-03-07 13:19:16 -08:00
|
|
|
dependencies.sdkSynchronizer = SDKSynchronizerDependency.mockWithSnapshot(.default)
|
2022-11-22 02:32:48 -08:00
|
|
|
dependencies.walletStorage = .noOp
|
2022-11-18 02:34:33 -08:00
|
|
|
}
|
2022-11-17 03:25:55 -08:00
|
|
|
|
2022-05-02 15:35:03 -07:00
|
|
|
// simulate the sending confirmation button to be pressed
|
2023-02-26 23:21:54 -08:00
|
|
|
_ = await store.send(.sendPressed) { state in
|
2023-01-02 07:18:58 -08:00
|
|
|
// once sending is confirmed, the attempts to try to send again by pressing the button
|
2022-05-02 15:35:03 -07:00
|
|
|
// needs to be eliminated, indicated by the flag `isSendingTransaction`, need to be true
|
|
|
|
state.isSendingTransaction = true
|
|
|
|
}
|
|
|
|
|
2022-11-22 02:32:48 -08:00
|
|
|
await testScheduler.advance(by: 0.01)
|
2023-02-26 23:21:54 -08:00
|
|
|
guard let memo = try? Memo(string: "") else {
|
2023-02-06 00:38:34 -08:00
|
|
|
XCTFail("testSendSucceeded: memo is expected to be successfuly initialized.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-05-02 15:35:03 -07:00
|
|
|
let transactionState = TransactionState(
|
2023-02-06 00:38:34 -08:00
|
|
|
expiryHeight: 40,
|
|
|
|
memos: [memo],
|
2022-05-02 15:35:03 -07:00
|
|
|
minedHeight: 50,
|
|
|
|
shielded: true,
|
|
|
|
zAddress: "tteafadlamnelkqe",
|
2022-07-19 11:56:46 -07:00
|
|
|
fee: Zatoshi(10),
|
2022-05-02 15:35:03 -07:00
|
|
|
id: "id",
|
|
|
|
status: .paid(success: true),
|
2022-06-23 02:05:34 -07:00
|
|
|
timestamp: 1234567,
|
2022-07-19 11:56:46 -07:00
|
|
|
zecAmount: Zatoshi(10)
|
2022-05-02 15:35:03 -07:00
|
|
|
)
|
|
|
|
|
2022-10-03 06:39:40 -07:00
|
|
|
// first it's expected that progress screen is showed
|
2022-12-01 06:31:30 -08:00
|
|
|
await store.receive(.updateDestination(.inProgress)) { state in
|
|
|
|
state.destination = .inProgress
|
2022-10-03 06:39:40 -07:00
|
|
|
}
|
|
|
|
|
2022-05-02 15:35:03 -07:00
|
|
|
// check the success transaction to be received back
|
2022-11-22 02:32:48 -08:00
|
|
|
await store.receive(.sendTransactionResult(Result.success(transactionState))) { state in
|
2022-05-02 15:35:03 -07:00
|
|
|
// from this moment on the sending next transaction is allowed again
|
|
|
|
// the 'isSendingTransaction' needs to be false again
|
|
|
|
state.isSendingTransaction = false
|
|
|
|
}
|
|
|
|
|
|
|
|
// all went well, the success screen is triggered
|
2022-12-01 06:31:30 -08:00
|
|
|
await store.receive(.updateDestination(.success)) { state in
|
|
|
|
state.destination = .success
|
2022-05-02 15:35:03 -07:00
|
|
|
}
|
|
|
|
}
|
2022-09-27 10:14:52 -07:00
|
|
|
|
2022-11-22 02:32:48 -08:00
|
|
|
@MainActor func testSendSucceededWithoutMemo() async throws {
|
|
|
|
// the test needs to pass the exportWallet() so we simulate some in the keychain
|
|
|
|
try storage.importWallet(bip39: "one two three", birthday: nil)
|
|
|
|
|
2022-09-27 10:14:52 -07:00
|
|
|
// setup the store and environment to be fully mocked
|
|
|
|
let testScheduler = DispatchQueue.test
|
|
|
|
|
2022-11-08 11:02:55 -08:00
|
|
|
var state = SendFlowReducer.State.placeholder
|
2022-09-27 10:14:52 -07:00
|
|
|
state.addMemoState = false
|
2022-11-22 02:32:48 -08:00
|
|
|
state.transactionAddressInputState = TransactionAddressTextFieldReducer.State(
|
|
|
|
textFieldState:
|
|
|
|
TCATextFieldReducer.State(
|
|
|
|
validationType: nil,
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "ztestsapling1psqa06alcfj9t6s246hht3n7kcw5h900r6z40qnuu7l58qs55kzeqa98879z9hzy596dca4hmsr".redacted
|
2022-11-22 02:32:48 -08:00
|
|
|
)
|
|
|
|
)
|
2022-09-27 10:14:52 -07:00
|
|
|
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: state,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) { dependencies in
|
2022-11-22 02:32:48 -08:00
|
|
|
dependencies.derivationTool = .liveValue
|
2022-11-18 02:34:33 -08:00
|
|
|
dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
|
2022-11-22 02:32:48 -08:00
|
|
|
dependencies.mnemonic = .liveValue
|
2023-03-07 13:19:16 -08:00
|
|
|
dependencies.sdkSynchronizer = SDKSynchronizerDependency.mockWithSnapshot(.default)
|
2022-11-22 02:32:48 -08:00
|
|
|
dependencies.walletStorage = .noOp
|
2022-11-18 02:34:33 -08:00
|
|
|
}
|
2022-09-27 10:14:52 -07:00
|
|
|
|
|
|
|
// simulate the sending confirmation button to be pressed
|
2023-02-26 23:21:54 -08:00
|
|
|
_ = await store.send(.sendPressed) { state in
|
2023-01-02 07:18:58 -08:00
|
|
|
// once sending is confirmed, the attempts to try to send again by pressing the button
|
2022-09-27 10:14:52 -07:00
|
|
|
// needs to be eliminated, indicated by the flag `isSendingTransaction`, need to be true
|
|
|
|
state.isSendingTransaction = true
|
|
|
|
}
|
|
|
|
|
2022-11-22 02:32:48 -08:00
|
|
|
await testScheduler.advance(by: 0.01)
|
2022-09-27 10:14:52 -07:00
|
|
|
|
|
|
|
let transactionState = TransactionState(
|
2023-02-06 00:38:34 -08:00
|
|
|
expiryHeight: 40,
|
|
|
|
memos: [],
|
2022-09-27 10:14:52 -07:00
|
|
|
minedHeight: 50,
|
|
|
|
shielded: true,
|
|
|
|
zAddress: "tteafadlamnelkqe",
|
|
|
|
fee: Zatoshi(10),
|
|
|
|
id: "id",
|
|
|
|
status: .paid(success: true),
|
|
|
|
timestamp: 1234567,
|
|
|
|
zecAmount: Zatoshi(10)
|
|
|
|
)
|
|
|
|
|
2022-10-03 06:39:40 -07:00
|
|
|
// first it's expected that progress screen is showed
|
2022-12-01 06:31:30 -08:00
|
|
|
await store.receive(.updateDestination(.inProgress)) { state in
|
|
|
|
state.destination = .inProgress
|
2022-10-03 06:39:40 -07:00
|
|
|
}
|
|
|
|
|
2022-09-27 10:14:52 -07:00
|
|
|
// check the success transaction to be received back
|
2022-11-22 02:32:48 -08:00
|
|
|
await store.receive(.sendTransactionResult(Result.success(transactionState))) { state in
|
2022-09-27 10:14:52 -07:00
|
|
|
// from this moment on the sending next transaction is allowed again
|
|
|
|
// the 'isSendingTransaction' needs to be false again
|
|
|
|
state.isSendingTransaction = false
|
|
|
|
}
|
|
|
|
|
|
|
|
// all went well, the success screen is triggered
|
2022-12-01 06:31:30 -08:00
|
|
|
await store.receive(.updateDestination(.success)) { state in
|
|
|
|
state.destination = .success
|
2022-09-27 10:14:52 -07:00
|
|
|
}
|
|
|
|
}
|
2022-05-02 15:35:03 -07:00
|
|
|
|
2022-11-22 02:32:48 -08:00
|
|
|
@MainActor func testSendFailed() async throws {
|
|
|
|
// the test needs to pass the exportWallet() so we simulate some in the keychain
|
|
|
|
try storage.importWallet(bip39: "one two three", birthday: nil)
|
|
|
|
|
2022-05-02 15:35:03 -07:00
|
|
|
// setup the store and environment to be fully mocked
|
|
|
|
let testScheduler = DispatchQueue.test
|
2022-05-11 08:26:39 -07:00
|
|
|
|
2022-11-22 02:32:48 -08:00
|
|
|
var initialState = SendFlowReducer.State.placeholder
|
|
|
|
initialState.transactionAddressInputState = TransactionAddressTextFieldReducer.State(
|
|
|
|
textFieldState:
|
|
|
|
TCATextFieldReducer.State(
|
|
|
|
validationType: nil,
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "ztestsapling1psqa06alcfj9t6s246hht3n7kcw5h900r6z40qnuu7l58qs55kzeqa98879z9hzy596dca4hmsr".redacted
|
2022-11-22 02:32:48 -08:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2022-05-02 15:35:03 -07:00
|
|
|
let store = TestStore(
|
2022-11-22 02:32:48 -08:00
|
|
|
initialState: initialState,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) { dependencies in
|
2022-11-22 02:32:48 -08:00
|
|
|
dependencies.derivationTool = .liveValue
|
2022-11-18 02:34:33 -08:00
|
|
|
dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
|
2022-11-22 02:32:48 -08:00
|
|
|
dependencies.mnemonic = .liveValue
|
|
|
|
dependencies.walletStorage = .noOp
|
2022-11-18 02:34:33 -08:00
|
|
|
}
|
2022-11-14 10:36:51 -08:00
|
|
|
|
2022-05-02 15:35:03 -07:00
|
|
|
// simulate the sending confirmation button to be pressed
|
2023-02-26 23:21:54 -08:00
|
|
|
_ = await store.send(.sendPressed) { state in
|
2023-01-02 07:18:58 -08:00
|
|
|
// once sending is confirmed, the attempts to try to send again by pressing the button
|
2022-05-02 15:35:03 -07:00
|
|
|
// needs to be eliminated, indicated by the flag `isSendingTransaction`, need to be true
|
|
|
|
state.isSendingTransaction = true
|
|
|
|
}
|
|
|
|
|
2022-11-22 02:32:48 -08:00
|
|
|
await testScheduler.advance(by: 0.01)
|
2022-05-02 15:35:03 -07:00
|
|
|
|
2022-10-03 06:39:40 -07:00
|
|
|
// first it's expected that progress screen is showed
|
2022-12-01 06:31:30 -08:00
|
|
|
await store.receive(.updateDestination(.inProgress)) { state in
|
|
|
|
state.destination = .inProgress
|
2022-10-03 06:39:40 -07:00
|
|
|
}
|
|
|
|
|
2022-05-02 15:35:03 -07:00
|
|
|
// check the failure transaction to be received back
|
2022-11-22 02:32:48 -08:00
|
|
|
await store.receive(.sendTransactionResult(Result.failure(SynchronizerError.criticalError as NSError))) { state in
|
2022-05-02 15:35:03 -07:00
|
|
|
// from this moment on the sending next transaction is allowed again
|
|
|
|
// the 'isSendingTransaction' needs to be false again
|
|
|
|
state.isSendingTransaction = false
|
|
|
|
}
|
|
|
|
|
|
|
|
// the failure screen is triggered as expected
|
2022-12-01 06:31:30 -08:00
|
|
|
await store.receive(.updateDestination(.failure)) { state in
|
|
|
|
state.destination = .failure
|
2022-05-02 15:35:03 -07:00
|
|
|
}
|
|
|
|
}
|
2022-05-11 08:26:39 -07:00
|
|
|
|
2022-11-18 02:34:33 -08:00
|
|
|
func testAddressValidation_Invalid() throws {
|
2022-05-11 08:26:39 -07:00
|
|
|
let store = TestStore(
|
|
|
|
initialState: .placeholder,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) { dependencies in
|
|
|
|
dependencies.derivationTool = .noOp
|
|
|
|
dependencies.derivationTool.isValidZcashAddress = { _ in false }
|
|
|
|
}
|
2022-11-14 10:36:51 -08:00
|
|
|
|
2023-02-13 11:32:50 -08:00
|
|
|
let address = "3HRG769ii3HDSJV5vNknQPzXqtL2mTSGnr".redacted
|
|
|
|
store.send(.transactionAddressInput(.textField(.set(address)))) { state in
|
|
|
|
state.transactionAddressInputState.textFieldState.text = address
|
2022-05-11 08:26:39 -07:00
|
|
|
// true is expected here because textField doesn't have any `validationType: String.ValidationType?`
|
2023-03-06 06:14:18 -08:00
|
|
|
// isValid function returns true, `guard let validationType else { return true }`
|
2022-05-11 08:26:39 -07:00
|
|
|
state.transactionAddressInputState.textFieldState.valid = true
|
|
|
|
state.transactionAddressInputState.isValidAddress = false
|
|
|
|
XCTAssertTrue(
|
|
|
|
state.isInvalidAddressFormat,
|
|
|
|
"Send Tests: `testAddressValidation` is expected to be true but it's \(state.isInvalidAddressFormat)"
|
|
|
|
)
|
|
|
|
}
|
2022-11-18 02:34:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func testAddressValidation_Valid() throws {
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: .placeholder,
|
|
|
|
reducer: SendFlowReducer()
|
|
|
|
) { dependencies in
|
|
|
|
dependencies.derivationTool = .noOp
|
|
|
|
dependencies.derivationTool.isValidZcashAddress = { _ in true }
|
|
|
|
}
|
2022-11-14 10:36:51 -08:00
|
|
|
|
2023-02-13 11:32:50 -08:00
|
|
|
let address = "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po".redacted
|
|
|
|
store.send(.transactionAddressInput(.textField(.set(address)))) { state in
|
|
|
|
state.transactionAddressInputState.textFieldState.text = address
|
2022-05-11 08:26:39 -07:00
|
|
|
// true is expected here because textField doesn't have any `validationType: String.ValidationType?`
|
2023-03-06 06:14:18 -08:00
|
|
|
// isValid function returns true, `guard let validationType else { return true }`
|
2022-05-11 08:26:39 -07:00
|
|
|
state.transactionAddressInputState.textFieldState.valid = true
|
|
|
|
state.transactionAddressInputState.isValidAddress = true
|
|
|
|
XCTAssertFalse(
|
|
|
|
state.isInvalidAddressFormat,
|
|
|
|
"Send Tests: `testAddressValidation` is expected to be false but it's \(state.isInvalidAddressFormat)"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testInvalidAmountFormatEmptyInput() throws {
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: .placeholder,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) {
|
|
|
|
$0.numberFormatter = .noOp
|
|
|
|
}
|
2022-11-17 03:25:55 -08:00
|
|
|
|
2022-05-11 08:26:39 -07:00
|
|
|
// Checks the computed property `isInvalidAmountFormat` which controls the error message to be shown on the screen
|
|
|
|
// With empty input it must be false
|
2023-02-13 11:32:50 -08:00
|
|
|
store.send(.transactionAmountInput(.textField(.set("".redacted))))
|
2022-05-31 10:14:56 -07:00
|
|
|
|
|
|
|
store.receive(.transactionAmountInput(.updateAmount))
|
2022-05-11 08:26:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func testInvalidAddressFormatEmptyInput() throws {
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: .placeholder,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) {
|
|
|
|
$0.derivationTool = .noOp
|
|
|
|
}
|
2022-11-14 10:36:51 -08:00
|
|
|
|
2022-05-11 08:26:39 -07:00
|
|
|
// Checks the computed property `isInvalidAddressFormat` which controls the error message to be shown on the screen
|
|
|
|
// With empty input it must be false
|
2023-02-13 11:32:50 -08:00
|
|
|
store.send(.transactionAddressInput(.textField(.set("".redacted)))) { state in
|
|
|
|
state.transactionAddressInputState.textFieldState.text = "".redacted
|
2022-05-11 08:26:39 -07:00
|
|
|
// true is expected here because textField doesn't have any `validationType: String.ValidationType?`
|
2023-03-06 06:14:18 -08:00
|
|
|
// isValid function returns true, `guard let validationType else { return true }`
|
2022-05-11 08:26:39 -07:00
|
|
|
state.transactionAddressInputState.textFieldState.valid = true
|
|
|
|
XCTAssertFalse(
|
|
|
|
state.isInvalidAddressFormat,
|
|
|
|
"Send Tests: `testInvalidAddressFormatEmptyInput` is expected to be false but it's \(state.isInvalidAddressFormat)"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-18 02:34:33 -08:00
|
|
|
func testFundsSufficiency_SufficientAmount() throws {
|
2022-11-08 11:02:55 -08:00
|
|
|
let sendState = SendFlowReducer.State(
|
2022-09-27 10:14:52 -07:00
|
|
|
addMemoState: true,
|
2022-08-01 23:07:03 -07:00
|
|
|
memoState: .placeholder,
|
2023-01-12 04:04:36 -08:00
|
|
|
scanState: .placeholder,
|
2022-05-11 08:26:39 -07:00
|
|
|
transactionAddressInputState: .placeholder,
|
|
|
|
transactionAmountInputState:
|
2022-11-08 00:36:03 -08:00
|
|
|
TransactionAmountTextFieldReducer.State(
|
2022-11-07 02:53:39 -08:00
|
|
|
currencySelectionState: CurrencySelectionReducer.State(),
|
2023-02-13 11:32:50 -08:00
|
|
|
maxValue: Int64(501_300).redacted,
|
2022-06-03 05:28:48 -07:00
|
|
|
textFieldState:
|
2022-11-05 08:47:11 -07:00
|
|
|
TCATextFieldReducer.State(
|
2022-06-03 05:28:48 -07:00
|
|
|
validationType: .customFloatingPoint(usNumberFormatter),
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "".redacted
|
2022-06-03 05:28:48 -07:00
|
|
|
)
|
2022-05-11 08:26:39 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: sendState,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) { dependencies in
|
|
|
|
dependencies.numberFormatter = .noOp
|
|
|
|
dependencies.numberFormatter.number = { _ in NSNumber(0.00501299) }
|
|
|
|
}
|
2022-11-17 03:25:55 -08:00
|
|
|
|
2023-02-13 11:32:50 -08:00
|
|
|
let address = "0.00501299".redacted
|
|
|
|
store.send(.transactionAmountInput(.textField(.set(address)))) { state in
|
|
|
|
state.transactionAmountInputState.textFieldState.text = address
|
2022-05-11 08:26:39 -07:00
|
|
|
state.transactionAmountInputState.textFieldState.valid = true
|
|
|
|
XCTAssertFalse(
|
|
|
|
state.isInsufficientFunds,
|
|
|
|
"Send Tests: `testFundsSufficiency` is expected to be false but it's \(state.isInsufficientFunds)"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-05-31 10:14:56 -07:00
|
|
|
store.receive(.transactionAmountInput(.updateAmount)) { state in
|
2023-02-13 11:32:50 -08:00
|
|
|
state.transactionAmountInputState.amount = Int64(501_299).redacted
|
2022-05-31 10:14:56 -07:00
|
|
|
}
|
2022-11-18 02:34:33 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func testFundsSufficiency_InsufficientAmount() throws {
|
|
|
|
let sendState = SendFlowReducer.State(
|
|
|
|
addMemoState: true,
|
|
|
|
memoState: .placeholder,
|
2023-01-12 04:04:36 -08:00
|
|
|
scanState: .placeholder,
|
2022-11-18 02:34:33 -08:00
|
|
|
transactionAddressInputState: .placeholder,
|
|
|
|
transactionAmountInputState:
|
|
|
|
TransactionAmountTextFieldReducer.State(
|
|
|
|
currencySelectionState: CurrencySelectionReducer.State(),
|
2023-02-13 11:32:50 -08:00
|
|
|
maxValue: Int64(501_300).redacted,
|
2022-11-18 02:34:33 -08:00
|
|
|
textFieldState:
|
|
|
|
TCATextFieldReducer.State(
|
|
|
|
validationType: .customFloatingPoint(usNumberFormatter),
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "".redacted
|
2022-11-18 02:34:33 -08:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: sendState,
|
|
|
|
reducer: SendFlowReducer()
|
|
|
|
) { dependencies in
|
|
|
|
dependencies.numberFormatter = .noOp
|
|
|
|
dependencies.numberFormatter.number = { _ in NSNumber(0.00501301) }
|
|
|
|
}
|
2022-11-17 03:25:55 -08:00
|
|
|
|
2023-02-13 11:32:50 -08:00
|
|
|
let value = "0.00501301".redacted
|
|
|
|
store.send(.transactionAmountInput(.textField(.set(value)))) { state in
|
|
|
|
state.transactionAmountInputState.textFieldState.text = value
|
2022-05-11 08:26:39 -07:00
|
|
|
state.transactionAmountInputState.textFieldState.valid = true
|
2022-05-31 10:14:56 -07:00
|
|
|
XCTAssertFalse(
|
|
|
|
state.isInsufficientFunds,
|
|
|
|
"Send Tests: `testFundsSufficiency` is expected to be false but it's \(state.isInsufficientFunds)"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
store.receive(.transactionAmountInput(.updateAmount)) { state in
|
2023-02-13 11:32:50 -08:00
|
|
|
state.transactionAmountInputState.amount = Int64(501_301).redacted
|
2022-05-11 08:26:39 -07:00
|
|
|
XCTAssertTrue(
|
|
|
|
state.isInsufficientFunds,
|
|
|
|
"Send Tests: `testFundsSufficiency` is expected to be true but it's \(state.isInsufficientFunds)"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-17 03:25:55 -08:00
|
|
|
func testDifferentNumberFormats_LiveNumberFormatter() throws {
|
|
|
|
try numberFormatTest("1.234", NSNumber(1.234))
|
|
|
|
try numberFormatTest("1,234", NSNumber(1_234))
|
|
|
|
try numberFormatTest("1 234", NSNumber(1_234))
|
|
|
|
try numberFormatTest("1,234.567", NSNumber(1_234.567))
|
|
|
|
try numberFormatTest("1.", NSNumber(1))
|
|
|
|
try numberFormatTest("1..", nil)
|
|
|
|
try numberFormatTest("1,.", nil)
|
|
|
|
try numberFormatTest("1.,", nil)
|
|
|
|
try numberFormatTest("1,,", nil)
|
|
|
|
try numberFormatTest("1,23", nil)
|
|
|
|
try numberFormatTest("1 23", nil)
|
|
|
|
try numberFormatTest("1.2.3", nil)
|
2022-05-11 08:26:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func testValidForm() throws {
|
2022-11-08 11:02:55 -08:00
|
|
|
let sendState = SendFlowReducer.State(
|
2022-09-27 10:14:52 -07:00
|
|
|
addMemoState: true,
|
2022-08-01 23:07:03 -07:00
|
|
|
memoState: .placeholder,
|
2023-01-12 04:04:36 -08:00
|
|
|
scanState: .placeholder,
|
2022-05-11 08:26:39 -07:00
|
|
|
transactionAddressInputState: .placeholder,
|
|
|
|
transactionAmountInputState:
|
2022-11-08 00:36:03 -08:00
|
|
|
TransactionAmountTextFieldReducer.State(
|
2023-02-13 11:32:50 -08:00
|
|
|
amount: Int64(501_301).redacted,
|
2022-11-07 02:53:39 -08:00
|
|
|
currencySelectionState: CurrencySelectionReducer.State(),
|
2023-02-13 11:32:50 -08:00
|
|
|
maxValue: Int64(501_302).redacted,
|
2022-05-11 08:26:39 -07:00
|
|
|
textFieldState:
|
2022-11-05 08:47:11 -07:00
|
|
|
TCATextFieldReducer.State(
|
2022-06-03 05:28:48 -07:00
|
|
|
validationType: .customFloatingPoint(usNumberFormatter),
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "0.00501301".redacted
|
2022-05-31 10:14:56 -07:00
|
|
|
)
|
2022-05-11 08:26:39 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: sendState,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) { dependencies in
|
|
|
|
dependencies.derivationTool = .noOp
|
|
|
|
dependencies.derivationTool.isValidZcashAddress = { _ in true }
|
|
|
|
}
|
2022-11-14 10:36:51 -08:00
|
|
|
|
2023-02-13 11:32:50 -08:00
|
|
|
let address = "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po".redacted
|
|
|
|
store.send(.transactionAddressInput(.textField(.set(address)))) { state in
|
|
|
|
state.transactionAddressInputState.textFieldState.text = address
|
2022-05-11 08:26:39 -07:00
|
|
|
// true is expected here because textField doesn't have any `validationType: String.ValidationType?`
|
2023-03-06 06:14:18 -08:00
|
|
|
// isValid function returns true, `guard let validationType else { return true }`
|
2022-05-11 08:26:39 -07:00
|
|
|
state.transactionAddressInputState.textFieldState.valid = true
|
|
|
|
state.transactionAddressInputState.isValidAddress = true
|
|
|
|
XCTAssertTrue(
|
|
|
|
state.isValidForm,
|
|
|
|
"Send Tests: `testValidForm` is expected to be true but it's \(state.isValidForm)"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testInvalidForm_InsufficientFunds() throws {
|
2022-11-08 11:02:55 -08:00
|
|
|
let sendState = SendFlowReducer.State(
|
2022-09-27 10:14:52 -07:00
|
|
|
addMemoState: true,
|
2022-08-01 23:07:03 -07:00
|
|
|
memoState: .placeholder,
|
2023-01-12 04:04:36 -08:00
|
|
|
scanState: .placeholder,
|
2022-05-11 08:26:39 -07:00
|
|
|
transactionAddressInputState: .placeholder,
|
|
|
|
transactionAmountInputState:
|
2022-11-08 00:36:03 -08:00
|
|
|
TransactionAmountTextFieldReducer.State(
|
2022-11-07 02:53:39 -08:00
|
|
|
currencySelectionState: CurrencySelectionReducer.State(),
|
2023-02-13 11:32:50 -08:00
|
|
|
maxValue: Int64(501_300).redacted,
|
2022-05-11 08:26:39 -07:00
|
|
|
textFieldState:
|
2022-11-05 08:47:11 -07:00
|
|
|
TCATextFieldReducer.State(
|
2022-05-11 08:26:39 -07:00
|
|
|
validationType: .floatingPoint,
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "0.00501301".redacted
|
2022-05-31 10:14:56 -07:00
|
|
|
)
|
2022-05-11 08:26:39 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: sendState,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) { dependencies in
|
|
|
|
dependencies.derivationTool = .noOp
|
|
|
|
dependencies.derivationTool.isValidZcashAddress = { _ in true }
|
|
|
|
}
|
2022-11-14 10:36:51 -08:00
|
|
|
|
2023-02-13 11:32:50 -08:00
|
|
|
let address = "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po".redacted
|
|
|
|
store.send(.transactionAddressInput(.textField(.set(address)))) { state in
|
|
|
|
state.transactionAddressInputState.textFieldState.text = address
|
2022-05-11 08:26:39 -07:00
|
|
|
// true is expected here because textField doesn't have any `validationType: String.ValidationType?`
|
2023-03-06 06:14:18 -08:00
|
|
|
// isValid function returns true, `guard let validationType else { return true }`
|
2022-05-11 08:26:39 -07:00
|
|
|
state.transactionAddressInputState.textFieldState.valid = true
|
|
|
|
state.transactionAddressInputState.isValidAddress = true
|
|
|
|
XCTAssertFalse(
|
|
|
|
state.isValidForm,
|
|
|
|
"Send Tests: `testValidForm` is expected to be false but it's \(state.isValidForm)"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testInvalidForm_AddressFormat() throws {
|
2022-11-08 11:02:55 -08:00
|
|
|
let sendState = SendFlowReducer.State(
|
2022-09-27 10:14:52 -07:00
|
|
|
addMemoState: true,
|
2022-08-01 23:07:03 -07:00
|
|
|
memoState: .placeholder,
|
2023-01-12 04:04:36 -08:00
|
|
|
scanState: .placeholder,
|
2022-05-11 08:26:39 -07:00
|
|
|
transactionAddressInputState: .placeholder,
|
|
|
|
transactionAmountInputState:
|
2022-11-08 00:36:03 -08:00
|
|
|
TransactionAmountTextFieldReducer.State(
|
2022-11-07 02:53:39 -08:00
|
|
|
currencySelectionState: CurrencySelectionReducer.State(),
|
2023-02-13 11:32:50 -08:00
|
|
|
maxValue: Int64(501_302).redacted,
|
2022-05-11 08:26:39 -07:00
|
|
|
textFieldState:
|
2022-11-05 08:47:11 -07:00
|
|
|
TCATextFieldReducer.State(
|
2022-05-11 08:26:39 -07:00
|
|
|
validationType: .floatingPoint,
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "0.00501301".redacted
|
2022-05-31 10:14:56 -07:00
|
|
|
)
|
2022-05-11 08:26:39 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: sendState,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) {
|
|
|
|
$0.derivationTool = .noOp
|
|
|
|
}
|
2022-11-14 10:36:51 -08:00
|
|
|
|
2023-02-13 11:32:50 -08:00
|
|
|
let address = "3HRG769ii3HDSJV5vNknQPzXqtL2mTSGnr".redacted
|
|
|
|
store.send(.transactionAddressInput(.textField(.set(address)))) { state in
|
|
|
|
state.transactionAddressInputState.textFieldState.text = address
|
2022-05-11 08:26:39 -07:00
|
|
|
// true is expected here because textField doesn't have any `validationType: String.ValidationType?`
|
2023-03-06 06:14:18 -08:00
|
|
|
// isValid function returns true, `guard let validationType else { return true }`
|
2022-05-11 08:26:39 -07:00
|
|
|
state.transactionAddressInputState.textFieldState.valid = true
|
|
|
|
state.transactionAddressInputState.isValidAddress = false
|
|
|
|
XCTAssertFalse(
|
|
|
|
state.isValidForm,
|
|
|
|
"Send Tests: `testValidForm` is expected to be false but it's \(state.isValidForm)"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testInvalidForm_AmountFormat() throws {
|
2022-11-08 11:02:55 -08:00
|
|
|
let sendState = SendFlowReducer.State(
|
2022-09-27 10:14:52 -07:00
|
|
|
addMemoState: true,
|
2022-08-01 23:07:03 -07:00
|
|
|
memoState: .placeholder,
|
2023-01-12 04:04:36 -08:00
|
|
|
scanState: .placeholder,
|
2022-05-11 08:26:39 -07:00
|
|
|
transactionAddressInputState: .placeholder,
|
|
|
|
transactionAmountInputState:
|
2022-11-08 00:36:03 -08:00
|
|
|
TransactionAmountTextFieldReducer.State(
|
2022-11-07 02:53:39 -08:00
|
|
|
currencySelectionState: CurrencySelectionReducer.State(),
|
2023-02-13 11:32:50 -08:00
|
|
|
maxValue: Int64(501_302).redacted,
|
2022-05-11 08:26:39 -07:00
|
|
|
textFieldState:
|
2022-11-05 08:47:11 -07:00
|
|
|
TCATextFieldReducer.State(
|
2022-05-11 08:26:39 -07:00
|
|
|
validationType: .floatingPoint,
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "0.0.0501301".redacted
|
2022-05-31 10:14:56 -07:00
|
|
|
)
|
2022-05-11 08:26:39 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: sendState,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-11-18 02:34:33 -08:00
|
|
|
) { dependencies in
|
|
|
|
dependencies.derivationTool = .noOp
|
|
|
|
dependencies.derivationTool.isValidZcashAddress = { _ in true }
|
|
|
|
}
|
2022-11-17 03:25:55 -08:00
|
|
|
|
2023-02-13 11:32:50 -08:00
|
|
|
let address = "tmGh6ttAnQRJra81moqYcedFadW9XtUT5Eq".redacted
|
|
|
|
store.send(.transactionAddressInput(.textField(.set(address)))) { state in
|
|
|
|
state.transactionAddressInputState.textFieldState.text = address
|
2022-05-11 08:26:39 -07:00
|
|
|
// true is expected here because textField doesn't have any `validationType: String.ValidationType?`
|
2023-03-06 06:14:18 -08:00
|
|
|
// isValid function returns true, `guard let validationType else { return true }`
|
2022-05-11 08:26:39 -07:00
|
|
|
state.transactionAddressInputState.textFieldState.valid = true
|
|
|
|
state.transactionAddressInputState.isValidAddress = true
|
|
|
|
XCTAssertFalse(
|
|
|
|
state.isValidForm,
|
|
|
|
"Send Tests: `testValidForm` is expected to be false but it's \(state.isValidForm)"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2022-08-01 23:07:03 -07:00
|
|
|
|
|
|
|
func testInvalidForm_ExceededMemoCharLimit() throws {
|
2022-11-08 11:02:55 -08:00
|
|
|
let sendState = SendFlowReducer.State(
|
2022-09-27 10:14:52 -07:00
|
|
|
addMemoState: true,
|
2022-11-07 02:58:56 -08:00
|
|
|
memoState: MultiLineTextFieldReducer.State(charLimit: 3),
|
2023-01-12 04:04:36 -08:00
|
|
|
scanState: .placeholder,
|
2023-02-13 11:32:50 -08:00
|
|
|
shieldedBalance: WalletBalance(verified: Zatoshi(1), total: Zatoshi(1)).redacted,
|
2022-08-01 23:07:03 -07:00
|
|
|
transactionAddressInputState:
|
2022-11-07 02:53:20 -08:00
|
|
|
TransactionAddressTextFieldReducer.State(
|
2022-08-01 23:07:03 -07:00
|
|
|
isValidAddress: true,
|
|
|
|
textFieldState:
|
2022-11-05 08:47:11 -07:00
|
|
|
TCATextFieldReducer.State(
|
2022-08-01 23:07:03 -07:00
|
|
|
validationType: .none,
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po".redacted
|
2022-08-01 23:07:03 -07:00
|
|
|
)
|
|
|
|
),
|
|
|
|
transactionAmountInputState:
|
2022-11-08 00:36:03 -08:00
|
|
|
TransactionAmountTextFieldReducer.State(
|
2023-02-13 11:32:50 -08:00
|
|
|
amount: Int64(100).redacted,
|
2022-11-07 02:53:39 -08:00
|
|
|
currencySelectionState: CurrencySelectionReducer.State(),
|
2023-02-13 11:32:50 -08:00
|
|
|
maxValue: Int64(501_302).redacted,
|
2022-08-01 23:07:03 -07:00
|
|
|
textFieldState:
|
2022-11-05 08:47:11 -07:00
|
|
|
TCATextFieldReducer.State(
|
2022-08-01 23:07:03 -07:00
|
|
|
validationType: .floatingPoint,
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "0.0.0501301".redacted
|
2022-08-01 23:07:03 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: sendState,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-08-01 23:07:03 -07:00
|
|
|
)
|
2022-11-17 03:25:55 -08:00
|
|
|
|
2023-02-13 11:32:50 -08:00
|
|
|
let value = "test".redacted
|
|
|
|
store.send(.memo(.memoInputChanged(value))) { state in
|
|
|
|
state.memoState.text = value
|
2022-08-01 23:07:03 -07:00
|
|
|
XCTAssertFalse(
|
|
|
|
state.isValidForm,
|
|
|
|
"Send Tests: `testValidForm` is expected to be false but it's \(state.isValidForm)"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testMemoCharLimitSet() throws {
|
2022-11-08 11:02:55 -08:00
|
|
|
let sendState = SendFlowReducer.State(
|
2022-09-27 10:14:52 -07:00
|
|
|
addMemoState: true,
|
2022-08-01 23:07:03 -07:00
|
|
|
memoState: .placeholder,
|
2023-01-12 04:04:36 -08:00
|
|
|
scanState: .placeholder,
|
2022-08-01 23:07:03 -07:00
|
|
|
transactionAddressInputState: .placeholder,
|
|
|
|
transactionAmountInputState:
|
2022-11-08 00:36:03 -08:00
|
|
|
TransactionAmountTextFieldReducer.State(
|
2022-11-07 02:53:39 -08:00
|
|
|
currencySelectionState: CurrencySelectionReducer.State(),
|
2023-02-13 11:32:50 -08:00
|
|
|
maxValue: Int64(501_302).redacted,
|
2022-08-01 23:07:03 -07:00
|
|
|
textFieldState:
|
2022-11-05 08:47:11 -07:00
|
|
|
TCATextFieldReducer.State(
|
2022-08-01 23:07:03 -07:00
|
|
|
validationType: .floatingPoint,
|
2023-02-13 11:32:50 -08:00
|
|
|
text: "0.0.0501301".redacted
|
2022-08-01 23:07:03 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: sendState,
|
2022-11-08 11:02:55 -08:00
|
|
|
reducer: SendFlowReducer()
|
2022-08-01 23:07:03 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
store.send(.onAppear) { state in
|
|
|
|
state.memoState.charLimit = 512
|
|
|
|
}
|
|
|
|
|
|
|
|
store.receive(.synchronizerStateChanged(.unknown))
|
|
|
|
|
|
|
|
// .onAppear action starts long living cancelable action .synchronizerStateChanged
|
|
|
|
// .onDisappear cancels it, must have for the test to pass
|
|
|
|
store.send(.onDisappear)
|
|
|
|
}
|
2023-01-12 04:04:36 -08:00
|
|
|
|
|
|
|
func testScannedAddress() throws {
|
|
|
|
let sendState = SendFlowReducer.State(
|
|
|
|
addMemoState: true,
|
|
|
|
memoState: .placeholder,
|
|
|
|
scanState: .placeholder,
|
|
|
|
transactionAddressInputState: .placeholder,
|
|
|
|
transactionAmountInputState: .placeholder
|
|
|
|
)
|
|
|
|
|
|
|
|
let store = TestStore(
|
|
|
|
initialState: sendState,
|
|
|
|
reducer: SendFlowReducer()
|
|
|
|
)
|
|
|
|
|
|
|
|
store.dependencies.audioServices = AudioServicesClient(systemSoundVibrate: { })
|
|
|
|
|
|
|
|
// We don't need to pass a valid address here, we just need to confirm some
|
|
|
|
// found string is received and the `isValidAddress` flag is set to `true`
|
2023-02-13 11:32:50 -08:00
|
|
|
let address = "address".redacted
|
|
|
|
store.send(.scan(.found(address))) { state in
|
|
|
|
state.transactionAddressInputState.textFieldState.text = address
|
2023-01-12 04:04:36 -08:00
|
|
|
state.transactionAddressInputState.isValidAddress = true
|
|
|
|
}
|
|
|
|
|
|
|
|
store.receive(.updateDestination(nil))
|
|
|
|
}
|
2022-05-11 08:26:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private extension SendTests {
|
2022-11-17 03:25:55 -08:00
|
|
|
func numberFormatTest(
|
2022-05-11 08:26:39 -07:00
|
|
|
_ amount: String,
|
2022-11-17 03:25:55 -08:00
|
|
|
_ expectedResult: NSNumber?
|
2022-05-11 08:26:39 -07:00
|
|
|
) throws {
|
2022-11-17 03:25:55 -08:00
|
|
|
if let number = NumberFormatterClient.liveValue.number(amount) {
|
|
|
|
XCTAssertEqual(number, expectedResult)
|
|
|
|
return
|
2022-05-31 10:14:56 -07:00
|
|
|
} else {
|
2022-11-17 03:25:55 -08:00
|
|
|
XCTAssertEqual(nil, expectedResult, "NumberFormatterClient.liveValue.number(\(amount)) unexpected result.")
|
2022-05-11 08:26:39 -07:00
|
|
|
}
|
|
|
|
}
|
2022-05-02 15:35:03 -07:00
|
|
|
}
|