[323] Unit/Integration tests for Home (#335)

- integration test for the .onAppear action
- unit tests for the synchronizer status change
- unit tests for the drawer and tracsation history
This commit is contained in:
Lukas Korba 2022-06-03 13:27:18 +02:00 committed by GitHub
parent b2ae82ce1b
commit 21806c737f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 245 additions and 15 deletions

View File

@ -102,6 +102,7 @@
9E391124283E4CAC0073DD9A /* ImportWalletTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E391123283E4CAC0073DD9A /* ImportWalletTests.swift */; };
9E391129283F74590073DD9A /* Zatoshi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E391128283F74590073DD9A /* Zatoshi.swift */; };
9E39112E283F91600073DD9A /* ZatoshiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E39112D283F91600073DD9A /* ZatoshiTests.swift */; };
9E3911392848AD500073DD9A /* HomeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911382848AD500073DD9A /* HomeTests.swift */; };
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */; };
9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; };
9E5BF63F2819542C00BA3F17 /* TransactionHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF63E2819542C00BA3F17 /* TransactionHistoryTests.swift */; };
@ -128,7 +129,7 @@
9E7FE0F92832824C00C374E8 /* QRCodeScanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0F82832824C00C374E8 /* QRCodeScanView.swift */; };
9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */; };
9E87ADF128363DE400122FCC /* WrappedAudioServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E87ADF028363DE400122FCC /* WrappedAudioServices.swift */; };
9EAFEB822805793200199FC9 /* AppReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB812805793200199FC9 /* AppReducerTests.swift */; };
9EAFEB822805793200199FC9 /* AppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB812805793200199FC9 /* AppTests.swift */; };
9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */; };
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */; };
9EAFEB882806E5AE00199FC9 /* WrappedSDKSynchronizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */; };
@ -292,6 +293,7 @@
9E391123283E4CAC0073DD9A /* ImportWalletTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportWalletTests.swift; sourceTree = "<group>"; };
9E391128283F74590073DD9A /* Zatoshi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Zatoshi.swift; sourceTree = "<group>"; };
9E39112D283F91600073DD9A /* ZatoshiTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZatoshiTests.swift; sourceTree = "<group>"; };
9E3911382848AD500073DD9A /* HomeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTests.swift; sourceTree = "<group>"; };
9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeumorphicDesignModifier.swift; sourceTree = "<group>"; };
9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantButtonStyles.swift; sourceTree = "<group>"; };
9E5BF63B2818305D00BA3F17 /* TransactionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionState.swift; sourceTree = "<group>"; };
@ -318,7 +320,7 @@
9E7FE0F82832824C00C374E8 /* QRCodeScanView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScanView.swift; sourceTree = "<group>"; };
9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = "<group>"; };
9E87ADF028363DE400122FCC /* WrappedAudioServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedAudioServices.swift; sourceTree = "<group>"; };
9EAFEB812805793200199FC9 /* AppReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducerTests.swift; sourceTree = "<group>"; };
9EAFEB812805793200199FC9 /* AppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTests.swift; sourceTree = "<group>"; };
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = "<group>"; };
9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItemTests.swift; sourceTree = "<group>"; };
9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSDKSynchronizer.swift; sourceTree = "<group>"; };
@ -478,11 +480,12 @@
0D4E7A1926B364180058B01E /* secantTests */ = {
isa = PBXGroup;
children = (
9E3911372848AD3A0073DD9A /* HomeTests */,
9E391122283E4C970073DD9A /* ImportWalletTests */,
9E01F8262833CD84000EFC57 /* ScanTests */,
9E5BF642281FEC8700BA3F17 /* SendTests */,
9E5BF63D281953F900BA3F17 /* TransactionHistoryTests */,
9EAFEB802805791400199FC9 /* AppReducerTests */,
9EAFEB802805791400199FC9 /* AppTests */,
9EF8135927ECC25E0075AF48 /* UtilTests */,
0DFE93E4272CB6D0000FCCA5 /* RecoveryPhraseValidationTests */,
0DFE93DD272C6D4B000FCCA5 /* BackupFlowTests */,
@ -757,6 +760,14 @@
path = ImportWalletTests;
sourceTree = "<group>";
};
9E3911372848AD3A0073DD9A /* HomeTests */ = {
isa = PBXGroup;
children = (
9E3911382848AD500073DD9A /* HomeTests.swift */,
);
path = HomeTests;
sourceTree = "<group>";
};
9E5BF63D281953F900BA3F17 /* TransactionHistoryTests */ = {
isa = PBXGroup;
children = (
@ -931,12 +942,12 @@
path = UIKitBridge;
sourceTree = "<group>";
};
9EAFEB802805791400199FC9 /* AppReducerTests */ = {
9EAFEB802805791400199FC9 /* AppTests */ = {
isa = PBXGroup;
children = (
9EAFEB812805793200199FC9 /* AppReducerTests.swift */,
9EAFEB812805793200199FC9 /* AppTests.swift */,
);
path = AppReducerTests;
path = AppTests;
sourceTree = "<group>";
};
9EAFEB8B2808174900199FC9 /* Sandbox */ = {
@ -1407,11 +1418,12 @@
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */,
9E5BF644281FEC9900BA3F17 /* SendTests.swift in Sources */,
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */,
9EAFEB822805793200199FC9 /* AppReducerTests.swift in Sources */,
9EAFEB822805793200199FC9 /* AppTests.swift in Sources */,
9E391124283E4CAC0073DD9A /* ImportWalletTests.swift in Sources */,
9E5BF63F2819542C00BA3F17 /* TransactionHistoryTests.swift in Sources */,
0D4E7A1B26B364180058B01E /* secantTests.swift in Sources */,
0DFE93E6272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift in Sources */,
9E3911392848AD500073DD9A /* HomeTests.swift in Sources */,
9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */,
9E02B56C27FED475005B809B /* DatabaseFilesTests.swift in Sources */,
9EF8135D27ECC25E0075AF48 /* UserPreferencesStorageTests.swift in Sources */,

View File

@ -301,11 +301,11 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
func getAllClearedTransactions() -> Effect<[TransactionState], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(amount: 1), status: .paid(success: false)),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(amount: 2)),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(amount: 3), status: .paid(success: true)),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(amount: 4)),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(amount: 5))
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(amount: 1), status: .paid(success: false), uuid: "1"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(amount: 2), uuid: "2"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(amount: 3), status: .paid(success: true), uuid: "3"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(amount: 4), uuid: "4"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(amount: 5), uuid: "5")
]
return Effect(
@ -316,7 +316,8 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
amount: $0.amount,
shielded: $0.shielded,
status: $0.status,
subtitle: $0.subtitle
subtitle: $0.subtitle,
uuid: $0.uuid
)
}
)

View File

@ -1,5 +1,5 @@
//
// AppReducerTests.swift
// AppTests.swift
// secantTests
//
// Created by Lukáš Korba on 12.04.2022.
@ -9,7 +9,7 @@ import XCTest
@testable import secant_testnet
import ComposableArchitecture
class AppReducerTests: XCTestCase {
class AppTests: XCTestCase {
static let testScheduler = DispatchQueue.test
let testEnvironment = AppEnvironment(

View File

@ -0,0 +1,217 @@
//
// HomeTests.swift
// secantTests
//
// Created by Lukáš Korba on 02.06.2022.
//
import XCTest
@testable import secant_testnet
import ComposableArchitecture
class HomeTests: XCTestCase {
func testSynchronizerStateChanged_AnyButSynced() throws {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let testEnvironment = HomeEnvironment(
audioServices: .silent,
derivationTool: .live(),
feedbackGenerator: .silent,
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
SDKSynchronizer: MockWrappedSDKSynchronizer(),
walletStorage: .throwing
)
let store = TestStore(
initialState: .placeholder,
reducer: HomeReducer.default,
environment: testEnvironment
)
store.send(.synchronizerStateChanged(.downloading))
store.receive(.updateSynchronizerStatus)
}
/// When the synchronizer status change to .synced, several things happen
/// 1. the .updateSynchronizerStatus is called
/// 2. the side effect to update the transactions history is called
/// 3. the side effect to update the balance is called
func testSynchronizerStateChanged_Synced() throws {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let testEnvironment = HomeEnvironment(
audioServices: .silent,
derivationTool: .live(),
feedbackGenerator: .silent,
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
SDKSynchronizer: MockWrappedSDKSynchronizer(),
walletStorage: .throwing
)
let store = TestStore(
initialState: .placeholder,
reducer: HomeReducer.default,
environment: testEnvironment
)
store.send(.synchronizerStateChanged(.synced))
testScheduler.advance(by: 0.01)
// ad 1.
store.receive(.updateSynchronizerStatus)
// ad 2.
let transactionsHelper: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(amount: 1), status: .paid(success: false), uuid: "1"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(amount: 2), uuid: "2"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(amount: 3), status: .paid(success: true), uuid: "3"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(amount: 4), uuid: "4"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(amount: 5), uuid: "5")
]
let transactions = transactionsHelper.map {
TransactionState.placeholder(
date: Date.init(timeIntervalSince1970: $0.date),
amount: $0.amount,
shielded: $0.shielded,
status: $0.status,
subtitle: $0.subtitle,
uuid: $0.uuid
)
}
store.receive(.updateTransactions(transactions))
// ad 3.
let balance = Balance(verified: 12_345_000, total: 12_345_000)
store.receive(.updateBalance(balance)) { state in
state.verifiedBalance = Zatoshi(amount: 12_345_000)
state.totalBalance = Zatoshi(amount: 12_345_000)
}
}
func testTransactionHistoryPartial_to_FullDrawer() throws {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let testEnvironment = HomeEnvironment(
audioServices: .silent,
derivationTool: .live(),
feedbackGenerator: .silent,
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
SDKSynchronizer: MockWrappedSDKSynchronizer(),
walletStorage: .throwing
)
let homeState = HomeState(
drawerOverlay: .partial,
profileState: .placeholder,
requestState: .placeholder,
sendState: .placeholder,
scanState: .placeholder,
synchronizerStatus: "",
totalBalance: Zatoshi.zero,
transactionHistoryState: .emptyPlaceHolder,
verifiedBalance: Zatoshi.zero
)
let store = TestStore(
initialState: homeState,
reducer: HomeReducer.default,
environment: testEnvironment
)
store.send(.transactionHistory(.updateRoute(.all))) { state in
state.transactionHistoryState.route = .all
}
store.receive(.updateDrawer(.full)) { state in
state.drawerOverlay = .full
state.transactionHistoryState.isScrollable = true
}
}
func testTransactionHistoryFull_to_PartialDrawer() throws {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let testEnvironment = HomeEnvironment(
audioServices: .silent,
derivationTool: .live(),
feedbackGenerator: .silent,
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
SDKSynchronizer: MockWrappedSDKSynchronizer(),
walletStorage: .throwing
)
let homeState = HomeState(
drawerOverlay: .full,
profileState: .placeholder,
requestState: .placeholder,
sendState: .placeholder,
scanState: .placeholder,
synchronizerStatus: "",
totalBalance: Zatoshi.zero,
transactionHistoryState: .emptyPlaceHolder,
verifiedBalance: Zatoshi.zero
)
let store = TestStore(
initialState: homeState,
reducer: HomeReducer.default,
environment: testEnvironment
)
store.send(.transactionHistory(.updateRoute(.latest))) { state in
state.transactionHistoryState.route = .latest
}
store.receive(.updateDrawer(.partial)) { state in
state.drawerOverlay = .partial
state.transactionHistoryState.isScrollable = false
}
}
/// The .onAppear action is important to register for the synchronizer state updates.
/// The integration tests make sure registrations and side effects are properly implemented.
func testOnAppear() throws {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let testEnvironment = HomeEnvironment(
audioServices: .silent,
derivationTool: .live(),
feedbackGenerator: .silent,
mnemonic: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
SDKSynchronizer: MockWrappedSDKSynchronizer(),
walletStorage: .throwing
)
let store = TestStore(
initialState: .placeholder,
reducer: HomeReducer.default,
environment: testEnvironment
)
store.send(.onAppear)
testScheduler.advance(by: 0.01)
// expected side effects as a result of .onAppear registration
store.receive(.synchronizerStateChanged(.unknown))
store.receive(.updateSynchronizerStatus)
// long-living (cancelable) effects need to be properly canceled.
// the .onDisappear action cancles the observer of the synchronizer status change.
store.send(.onDisappear)
}
}