From 49d858d22a1f56fd31269e574eb001eb232a2850 Mon Sep 17 00:00:00 2001 From: Lukas Korba Date: Thu, 2 Mar 2023 15:24:32 +0100 Subject: [PATCH] [#557] Nav Changes (#602) - previous profile screen connected to the receive ZEC button - receive ZEC is now simplified to show only QR code + UA with small "i" icon leading to address details - profile's UA address copy to pasteboard added - home's settings button connected to settings screen - settings screen updated, test crash report and rescan blockchain moved to debug menu - root reducer's debug code move to a separate file - unit tests updated + debug tests provided --- secant.xcodeproj/project.pbxproj | 10 ++ .../CrashReporter/CrashReporterLiveKey.swift | 2 + .../CrashReporter/CrashReporterTestKey.swift | 1 + .../CrashReportingInterface.swift | 1 + secant/Features/Home/HomeStore.swift | 66 +++------- secant/Features/Home/HomeView.swift | 38 +++++- secant/Features/Profile/ProfileStore.swift | 33 ++--- secant/Features/Profile/ProfileView.swift | 75 ++++------- secant/Features/Root/RootDebug.swift | 117 ++++++++++++++++++ secant/Features/Root/RootDestination.swift | 4 +- secant/Features/Root/RootInitialization.swift | 43 ++----- secant/Features/Root/RootStore.swift | 5 + secant/Features/Root/RootView.swift | 12 ++ secant/Features/Settings/SettingsStore.swift | 26 ---- secant/Features/Settings/SettingsView.swift | 18 --- secantTests/HomeTests/HomeTests.swift | 50 -------- secantTests/ProfileTests/ProfileTests.swift | 28 ++++- ...PhraseValidationFlowFeatureFlagTests.swift | 1 + .../RootTests/AppInitializationTests.swift | 1 + secantTests/RootTests/DebugTests.swift | 107 ++++++++++++++++ secantTests/SettingsTests/SettingsTests.swift | 113 +---------------- .../HomeSnapshotTests/HomeSnapshotTests.swift | 2 +- .../WalletEventsSnapshotTests.swift | 8 +- .../WalletConfigProviderTests.swift | 4 +- 24 files changed, 384 insertions(+), 381 deletions(-) create mode 100644 secant/Features/Root/RootDebug.swift create mode 100644 secantTests/RootTests/DebugTests.swift diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index 9037c84..8435beb 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -447,6 +447,9 @@ 9E7FE0F628327F6F00C374E8 /* ScanUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0F528327F6F00C374E8 /* ScanUIView.swift */; }; 9E7FE0F92832824C00C374E8 /* QRCodeScanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0F82832824C00C374E8 /* QRCodeScanView.swift */; }; 9E852D5C29AF8EB200CF4AC1 /* RecoveryPhraseValidationFlowFeatureFlagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E852D5B29AF8EB200CF4AC1 /* RecoveryPhraseValidationFlowFeatureFlagTests.swift */; }; + 9E852D6129B098F400CF4AC1 /* RootDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E852D6029B098F400CF4AC1 /* RootDebug.swift */; }; + 9E852D6229B098F400CF4AC1 /* RootDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E852D6029B098F400CF4AC1 /* RootDebug.swift */; }; + 9E852D6529B0A86300CF4AC1 /* DebugTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E852D6429B0A86300CF4AC1 /* DebugTests.swift */; }; 9E92AF0828530EBF007367AD /* View+UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E92AF0728530EBF007367AD /* View+UIImage.swift */; }; 9E94C62028AA7DEE008256E9 /* BalanceBreakdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E94C61F28AA7DEE008256E9 /* BalanceBreakdownTests.swift */; }; 9E94C62328AA7EE0008256E9 /* BalanceBreakdownSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E94C62228AA7EE0008256E9 /* BalanceBreakdownSnapshotTests.swift */; }; @@ -770,6 +773,8 @@ 9E7FE0F528327F6F00C374E8 /* ScanUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanUIView.swift; sourceTree = ""; }; 9E7FE0F82832824C00C374E8 /* QRCodeScanView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScanView.swift; sourceTree = ""; }; 9E852D5B29AF8EB200CF4AC1 /* RecoveryPhraseValidationFlowFeatureFlagTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseValidationFlowFeatureFlagTests.swift; sourceTree = ""; }; + 9E852D6029B098F400CF4AC1 /* RootDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootDebug.swift; sourceTree = ""; }; + 9E852D6429B0A86300CF4AC1 /* DebugTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugTests.swift; sourceTree = ""; }; 9E92AF0728530EBF007367AD /* View+UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+UIImage.swift"; sourceTree = ""; }; 9E94C61F28AA7DEE008256E9 /* BalanceBreakdownTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceBreakdownTests.swift; sourceTree = ""; }; 9E94C62228AA7EE0008256E9 /* BalanceBreakdownSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceBreakdownSnapshotTests.swift; sourceTree = ""; }; @@ -1852,6 +1857,7 @@ children = ( 9EAFEB812805793200199FC9 /* RootTests.swift */, 9E391131284644580073DD9A /* AppInitializationTests.swift */, + 9E852D6429B0A86300CF4AC1 /* DebugTests.swift */, ); path = RootTests; sourceTree = ""; @@ -2104,6 +2110,7 @@ F9971A4C27680DC400A2DB75 /* RootView.swift */, 9E9ADA7E2938F5EC0071767B /* RootDestination.swift */, 9E9ADA7C2938F4C00071767B /* RootInitialization.swift */, + 9E852D6029B098F400CF4AC1 /* RootDebug.swift */, ); path = Root; sourceTree = ""; @@ -2686,6 +2693,7 @@ 0D26AF01299E8196005260EE /* RecoveryPhraseDisplayView.swift in Sources */, 0D26AF02299E8196005260EE /* URIParser.swift in Sources */, 0D26AF03299E8196005260EE /* URIParserLive.swift in Sources */, + 9E852D6229B098F400CF4AC1 /* RootDebug.swift in Sources */, 34F682ED29A763FD0022C079 /* WalletConfigProvider.swift in Sources */, 0D26AF04299E8196005260EE /* LocalAuthenticationTestKey.swift in Sources */, 0D26AF05299E8196005260EE /* ScanView.swift in Sources */, @@ -2915,6 +2923,7 @@ 0D3D04082728B3440032ABC1 /* RecoveryPhraseDisplayView.swift in Sources */, 9EB863A2292398A8003D0F8B /* URIParser.swift in Sources */, 9EB863C12923C779003D0F8B /* URIParserLive.swift in Sources */, + 9E852D6129B098F400CF4AC1 /* RootDebug.swift in Sources */, 34F682EC29A763FD0022C079 /* WalletConfigProvider.swift in Sources */, 9EBDF987291F91EF000A1A05 /* LocalAuthenticationTestKey.swift in Sources */, F9971A5F27680DF600A2DB75 /* ScanView.swift in Sources */, @@ -3053,6 +3062,7 @@ 9EAFEB862805A23100199FC9 /* SecItemClientTests.swift in Sources */, 3448CB3728E485CB006ADEDB /* NotEnoughFeeSpaceSnapshots.swift in Sources */, 9E9ECC9828589E150099D5A2 /* WelcomeSnapshotTests.swift in Sources */, + 9E852D6529B0A86300CF4AC1 /* DebugTests.swift in Sources */, 9E7CB6122869882D00A02233 /* WalletEventsSnapshotTests.swift in Sources */, 9E5BF644281FEC9900BA3F17 /* SendTests.swift in Sources */, 9E852D5C29AF8EB200CF4AC1 /* RecoveryPhraseValidationFlowFeatureFlagTests.swift in Sources */, diff --git a/secant/Dependencies/CrashReporter/CrashReporterLiveKey.swift b/secant/Dependencies/CrashReporter/CrashReporterLiveKey.swift index c23cb0b..2d6bb43 100644 --- a/secant/Dependencies/CrashReporter/CrashReporterLiveKey.swift +++ b/secant/Dependencies/CrashReporter/CrashReporterLiveKey.swift @@ -4,9 +4,11 @@ // // Created by Francisco Gindre on 2/2/23. // + import ComposableArchitecture import FirebaseCore import FirebaseCrashlytics + extension CrashReporterClient: DependencyKey { static let liveValue = CrashReporterClient( configure: { canConfigure in diff --git a/secant/Dependencies/CrashReporter/CrashReporterTestKey.swift b/secant/Dependencies/CrashReporter/CrashReporterTestKey.swift index dc6483c..6becd16 100644 --- a/secant/Dependencies/CrashReporter/CrashReporterTestKey.swift +++ b/secant/Dependencies/CrashReporter/CrashReporterTestKey.swift @@ -6,6 +6,7 @@ // import ComposableArchitecture + extension CrashReporterClient: TestDependencyKey { static let testValue = CrashReporterClient( configure: { _ in }, diff --git a/secant/Dependencies/CrashReporter/CrashReportingInterface.swift b/secant/Dependencies/CrashReporter/CrashReportingInterface.swift index 9d85c54..b708866 100644 --- a/secant/Dependencies/CrashReporter/CrashReportingInterface.swift +++ b/secant/Dependencies/CrashReporter/CrashReportingInterface.swift @@ -4,6 +4,7 @@ // // Created by Francisco Gindre on 2/2/23. // + import ComposableArchitecture import Foundation diff --git a/secant/Features/Home/HomeStore.swift b/secant/Features/Home/HomeStore.swift index c271a95..fc1eed2 100644 --- a/secant/Features/Home/HomeStore.swift +++ b/secant/Features/Home/HomeStore.swift @@ -16,8 +16,8 @@ struct HomeReducer: ReducerProtocol { case balanceBreakdown case notEnoughFreeDiskSpace case profile - case request case send + case settings case transactionHistory } @@ -25,10 +25,10 @@ struct HomeReducer: ReducerProtocol { var balanceBreakdownState: BalanceBreakdownReducer.State var destination: Destination? var profileState: ProfileReducer.State - var requestState: RequestReducer.State var requiredTransactionConfirmations = 0 var scanState: ScanReducer.State var sendState: SendFlowReducer.State + var settingsState: SettingsReducer.State var shieldedBalance: Balance var synchronizerStatusSnapshot: SyncStatusSnapshot var walletEventsState: WalletEventsFlowReducer.State @@ -61,9 +61,8 @@ struct HomeReducer: ReducerProtocol { case onAppear case onDisappear case profile(ProfileReducer.Action) - case request(RequestReducer.Action) - case rewindDone(String?, SettingsReducer.Action) case send(SendFlowReducer.Action) + case settings(SettingsReducer.Action) case synchronizerStateChanged(SDKSynchronizerState) case walletEvents(WalletEventsFlowReducer.Action) case updateDestination(HomeReducer.State.Destination?) @@ -86,6 +85,10 @@ struct HomeReducer: ReducerProtocol { SendFlowReducer() } + Scope(state: \.settingsState, action: /Action.settings) { + SettingsReducer() + } + Scope(state: \.profileState, action: /Action.profile) { ProfileReducer() } @@ -137,45 +140,12 @@ struct HomeReducer: ReducerProtocol { case .profile(.back): state.destination = nil return .none - - case .profile(.settings(.quickRescan)): - state.destination = nil - return .run { send in - do { - try await sdkSynchronizer.rewind(.quick) - await send(.rewindDone(nil, .quickRescan)) - } catch { - await send(.rewindDone(error.localizedDescription, .quickRescan)) - } - } - - case .profile(.settings(.fullRescan)): - state.destination = nil - return .run { send in - do { - try await sdkSynchronizer.rewind(.birthday) - await send(.rewindDone(nil, .fullRescan)) - } catch { - await send(.rewindDone(error.localizedDescription, .fullRescan)) - } - } + + case .settings: + return .none case .profile: return .none - - case .request: - return .none - - case let .rewindDone(errorDescription, _): - if let errorDescription { - // TODO: [#221] Handle error more properly (https://github.com/zcash/secant-ios-wallet/issues/221) - state.alert = AlertState( - title: TextState("Rewind failed"), - message: TextState("Error: \(errorDescription)"), - dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert)) - ) - } - return .none case .walletEvents: return .none @@ -221,13 +191,6 @@ extension HomeStore { ) } - func requestStore() -> RequestStore { - self.scope( - state: \.requestState, - action: HomeReducer.Action.request - ) - } - func sendStore() -> SendFlowStore { self.scope( state: \.sendState, @@ -235,6 +198,13 @@ extension HomeStore { ) } + func settingsStore() -> SettingsStore { + self.scope( + state: \.settingsState, + action: HomeReducer.Action.settings + ) + } + func balanceBreakdownStore() -> BalanceBreakdownStore { self.scope( state: \.balanceBreakdownState, @@ -263,9 +233,9 @@ extension HomeReducer.State { .init( balanceBreakdownState: .placeholder, profileState: .placeholder, - requestState: .placeholder, scanState: .placeholder, sendState: .placeholder, + settingsState: .placeholder, shieldedBalance: Balance.zero, synchronizerStatusSnapshot: .default, walletEventsState: .emptyPlaceHolder diff --git a/secant/Features/Home/HomeView.swift b/secant/Features/Home/HomeView.swift index 09b6b87..8a9bae2 100644 --- a/secant/Features/Home/HomeView.swift +++ b/secant/Features/Home/HomeView.swift @@ -8,9 +8,9 @@ struct HomeView: View { WithViewStore(store) { viewStore in VStack { HStack { - profileButton(viewStore) - Spacer() + + settingsButton(viewStore) } balance(viewStore) @@ -18,6 +18,8 @@ struct HomeView: View { Spacer() sendButton(viewStore) + + receiveButton(viewStore) Button { viewStore.send(.updateDestination(.transactionHistory)) @@ -48,21 +50,21 @@ struct HomeView: View { // MARK: - Buttons extension HomeView { - func profileButton(_ viewStore: HomeViewStore) -> some View { + func settingsButton(_ viewStore: HomeViewStore) -> some View { Image(Asset.Assets.Icons.profile.name) .resizable() .frame(width: 60, height: 60) .padding(.trailing, 15) .navigationLink( - isActive: viewStore.bindingForDestination(.profile), + isActive: viewStore.bindingForDestination(.settings), destination: { - ProfileView(store: store.profileStore()) + SettingsView(store: store.settingsStore()) } ) } func sendButton(_ viewStore: HomeViewStore) -> some View { - Text("Send") + Text("Send ZEC") .shadow(color: Asset.Colors.Buttons.buttonsTitleShadow.color, radius: 2, x: 0, y: 2) .frame( minWidth: 0, @@ -85,6 +87,30 @@ extension HomeView { .padding(.bottom, 30) } + func receiveButton(_ viewStore: HomeViewStore) -> some View { + Text("Receive ZEC") + .shadow(color: Asset.Colors.Buttons.buttonsTitleShadow.color, radius: 2, x: 0, y: 2) + .frame( + minWidth: 0, + maxWidth: .infinity, + minHeight: 0, + maxHeight: .infinity + ) + .foregroundColor(Asset.Colors.Text.activeButtonText.color) + .background(Asset.Colors.Buttons.activeButton.color) + .cornerRadius(12) + .frame(height: 60) + .padding(.horizontal, 50) + .neumorphicButton() + .navigationLink( + isActive: viewStore.bindingForDestination(.profile), + destination: { + ProfileView(store: store.profileStore()) + } + ) + .padding(.bottom, 30) + } + func balance(_ viewStore: HomeViewStore) -> some View { Group { Button { diff --git a/secant/Features/Profile/ProfileStore.swift b/secant/Features/Profile/ProfileStore.swift index e88d2ab..121e83c 100644 --- a/secant/Features/Profile/ProfileStore.swift +++ b/secant/Features/Profile/ProfileStore.swift @@ -9,7 +9,6 @@ struct ProfileReducer: ReducerProtocol { struct State: Equatable { enum Destination { case addressDetails - case settings } var addressDetailsState: AddressDetailsReducer.State @@ -17,7 +16,6 @@ struct ProfileReducer: ReducerProtocol { var appVersion = "" var destination: Destination? var sdkVersion = "" - var settingsState: SettingsReducer.State var unifiedAddress: String { addressDetailsState.uAddress?.stringEncoded ?? "could not extract UA" @@ -27,12 +25,13 @@ struct ProfileReducer: ReducerProtocol { enum Action: Equatable { case addressDetails(AddressDetailsReducer.Action) case back + case copyUnifiedAddressToPastboard case onAppear - case settings(SettingsReducer.Action) case updateDestination(ProfileReducer.State.Destination?) } @Dependency(\.appVersion) var appVersion + @Dependency(\.pasteboard) var pasteboard @Dependency(\.sdkSynchronizer) var sdkSynchronizer @Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment @@ -41,10 +40,6 @@ struct ProfileReducer: ReducerProtocol { AddressDetailsReducer() } - Scope(state: \.settingsState, action: /Action.settings) { - SettingsReducer() - } - Reduce { state, action in switch action { case .onAppear: @@ -56,6 +51,10 @@ struct ProfileReducer: ReducerProtocol { case .back: return .none + + case .copyUnifiedAddressToPastboard: + pasteboard.setString(state.unifiedAddress.redacted) + return .none case let .updateDestination(destination): state.destination = destination @@ -63,9 +62,6 @@ struct ProfileReducer: ReducerProtocol { case .addressDetails: return .none - - case .settings: - return .none } } } @@ -80,13 +76,6 @@ extension ProfileStore { action: ProfileReducer.Action.addressDetails ) } - - func settingsStore() -> SettingsStore { - self.scope( - state: \.settingsState, - action: ProfileReducer.Action.settings - ) - } } // MARK: - ViewStore @@ -105,13 +94,6 @@ extension ProfileViewStore { embed: { $0 ? .addressDetails : nil } ) } - - var bindingForSettings: Binding { - self.destinationBinding.map( - extract: { $0 == .settings }, - embed: { $0 ? .settings : nil } - ) - } } // MARK: - Placeholders @@ -120,8 +102,7 @@ extension ProfileReducer.State { static var placeholder: Self { .init( addressDetailsState: .placeholder, - destination: nil, - settingsState: .placeholder + destination: nil ) } } diff --git a/secant/Features/Profile/ProfileView.swift b/secant/Features/Profile/ProfileView.swift index 4ce4795..b021837 100644 --- a/secant/Features/Profile/ProfileView.swift +++ b/secant/Features/Profile/ProfileView.swift @@ -6,62 +6,32 @@ struct ProfileView: View { var body: some View { WithViewStore(store) { viewStore in - ScrollView { + VStack { qrCodeUA(viewStore.unifiedAddress) - .padding(.top, 30) - - Text("Your UA address \(viewStore.unifiedAddress)") - .truncationMode(.middle) - .multilineTextAlignment(.center) - .lineLimit(2) - .padding(30) + .padding(.vertical, 50) - Button( - action: { viewStore.send(.updateDestination(.addressDetails)) }, - label: { Text("See address details") } - ) - .activeButtonStyle - .frame(height: 50) - .padding(EdgeInsets(top: 0, leading: 30, bottom: 50, trailing: 30)) - - Rectangle() - .frame(height: 1.5) - .padding(EdgeInsets(top: 0, leading: 100, bottom: 20, trailing: 100)) - .foregroundColor(Asset.Colors.TextField.Underline.purple.color) - - Button( - action: { viewStore.send(.updateDestination(.settings)) }, - label: { Text("Settings") } - ) - .primaryButtonStyle - .frame(height: 50) - .padding(EdgeInsets(top: 30, leading: 30, bottom: 20, trailing: 30)) - - Spacer() - HStack { - VStack { - Text("secant v\(viewStore.appVersion)(\(viewStore.appBuild))") - Text("sdk v\(viewStore.sdkVersion)") - } - Spacer() - Button( - action: { }, - label: { - Text("More info") - .foregroundColor(Asset.Colors.Text.moreInfoText.color) + Text("Your UA") + .fontWeight(.bold) + .onTapGesture { + viewStore.send(.copyUnifiedAddressToPastboard) } - ) + + Button { + viewStore.send(.updateDestination(.addressDetails)) + } label: { + Image(systemName: "info.circle") + .offset(x: -10, y: -10) + .tint(.black) + } } - .padding(30) + + Text("\(viewStore.unifiedAddress)") + .padding(30) + + Spacer() } .onAppear(perform: { viewStore.send(.onAppear) }) - .navigationLinkEmpty( - isActive: viewStore.bindingForSettings, - destination: { - SettingsView(store: store.settingsStore()) - } - ) .navigationLinkEmpty( isActive: viewStore.bindingForAddressDetails, destination: { @@ -104,14 +74,11 @@ struct ProfileView_Previews: PreviewProvider { NavigationView { ProfileView( store: .init( - initialState: .init( - addressDetailsState: .placeholder, - settingsState: .placeholder - ), + initialState: .init(addressDetailsState: .placeholder), reducer: ProfileReducer() ) ) } - .preferredColorScheme(.dark) + .preferredColorScheme(.light) } } diff --git a/secant/Features/Root/RootDebug.swift b/secant/Features/Root/RootDebug.swift new file mode 100644 index 0000000..f0cc459 --- /dev/null +++ b/secant/Features/Root/RootDebug.swift @@ -0,0 +1,117 @@ +// +// RootDebug.swift +// secant +// +// Created by Lukáš Korba on 02.03.2023. +// + +import Foundation +import ComposableArchitecture +import ZcashLightClientKit + +/// In this file is a collection of helpers that control all state and action related operations +/// for the `RootReducer` with a connection to the UI navigation. +extension RootReducer { + struct DebugState: Equatable { + var rescanDialog: ConfirmationDialogState? + } + + indirect enum DebugAction: Equatable { + case cancelRescan + case flagUpdated + case fullRescan + case quickRescan + case rescanBlockchain + case rewindDone(String?, RootReducer.Action) + case testCrashReporter // this will crash the app if live. + case updateFlag(FeatureFlag, Bool) + case walletConfigLoaded(WalletConfig) + } + + // swiftlint:disable:next cyclomatic_complexity + func debugReduce() -> Reduce { + Reduce { state, action in + switch action { + case .debug(.testCrashReporter): + crashReporter.testCrash() + return .none + + case .debug(.rescanBlockchain): + state.debugState.rescanDialog = .init( + title: TextState("Rescan"), + message: TextState("Select the rescan you want"), + buttons: [ + .default(TextState("Quick rescan"), action: .send(.debug(.quickRescan))), + .default(TextState("Full rescan"), action: .send(.debug(.fullRescan))), + .cancel(TextState("Cancel")) + ] + ) + return .none + + case .debug(.cancelRescan): + state.debugState.rescanDialog = nil + return .none + + case .debug(.quickRescan): + state.destinationState.destination = .home + return .run { send in + do { + try await sdkSynchronizer.rewind(.quick) + await send(.debug(.rewindDone(nil, .debug(.quickRescan)))) + } catch { + await send(.debug(.rewindDone(error.localizedDescription, .debug(.quickRescan)))) + } + } + + case .debug(.fullRescan): + state.destinationState.destination = .home + return .run { send in + do { + try await sdkSynchronizer.rewind(.birthday) + await send(.debug(.rewindDone(nil, .debug(.fullRescan)))) + } catch { + await send(.debug(.rewindDone(error.localizedDescription, .debug(.fullRescan)))) + } + } + + case let .debug(.rewindDone(errorDescription, _)): + if let errorDescription { + // TODO: [#221] Handle error more properly (https://github.com/zcash/secant-ios-wallet/issues/221) + state.alert = AlertState( + title: TextState("Rewind failed"), + message: TextState("Error: \(errorDescription)"), + dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert)) + ) + } + return .none + + case let .debug(.updateFlag(flag, isEnabled)): + return walletConfigProvider.update(flag, !isEnabled) + .receive(on: mainQueue) + .map { _ in return Action.debug(.flagUpdated) } + .eraseToEffect() + .cancellable(id: WalletConfigCancelId.self, cancelInFlight: true) + + case .debug(.flagUpdated): + return walletConfigProvider.load() + .receive(on: mainQueue) + .map { Action.debug(.walletConfigLoaded($0)) } + .eraseToEffect() + .cancellable(id: WalletConfigCancelId.self, cancelInFlight: true) + + case let .debug(.walletConfigLoaded(walletConfig)): + return EffectTask(value: .updateStateAfterConfigUpdate(walletConfig)) + + default: return .none + } + } + } +} + +// MARK: Placeholders + +extension RootReducer.DebugState { + static var placeholder: Self { + .init() + } +} diff --git a/secant/Features/Root/RootDestination.swift b/secant/Features/Root/RootDestination.swift index 9c189d9..a85a8f6 100644 --- a/secant/Features/Root/RootDestination.swift +++ b/secant/Features/Root/RootDestination.swift @@ -134,8 +134,8 @@ extension RootReducer { state.destinationState.alert = nil return .none - case .home, .initialization, .onboarding, .phraseDisplay, .phraseValidation, - .sandbox, .welcome, .binding, .nukeWalletFailed, .nukeWalletSucceeded, .debug, .walletConfigLoaded, .dismissAlert: + case .home, .initialization, .onboarding, .phraseDisplay, .phraseValidation, .sandbox, .updateStateAfterConfigUpdate, + .welcome, .binding, .nukeWalletFailed, .nukeWalletSucceeded, .debug, .walletConfigLoaded, .dismissAlert: return .none } diff --git a/secant/Features/Root/RootInitialization.swift b/secant/Features/Root/RootInitialization.swift index 8fe959c..ae19acc 100644 --- a/secant/Features/Root/RootInitialization.swift +++ b/secant/Features/Root/RootInitialization.swift @@ -26,12 +26,6 @@ extension RootReducer { case walletConfigChanged(WalletConfig) } - enum DebugAction: Equatable { - case updateFlag(FeatureFlag, Bool) - case flagUpdated - case walletConfigLoaded(WalletConfig) - } - // swiftlint:disable:next cyclomatic_complexity function_body_length func initializationReduce() -> Reduce { Reduce { state, action in @@ -56,8 +50,10 @@ extension RootReducer { } case .initialization(.walletConfigChanged(let walletConfig)): - updateStateAfterConfigUpdate(state: &state, config: walletConfig) - return EffectTask(value: .initialization(.initialSetups)) + return .concatenate( + EffectTask(value: .updateStateAfterConfigUpdate(walletConfig)), + EffectTask(value: .initialization(.initialSetups)) + ) case .initialization(.initialSetups): // TODO: [#524] finish all the wallet events according to definition, https://github.com/zcash/secant-ios-wallet/issues/524 @@ -307,43 +303,26 @@ extension RootReducer { case .onboarding(.createNewWallet): return EffectTask(value: .initialization(.createNewWallet)) - - case .home, .destination, .onboarding, .phraseDisplay, .phraseValidation, .sandbox, .welcome, .binding: - return .none case .initialization(.configureCrashReporter): crashReporter.configure( !userStoredPreferences.isUserOptedOutOfCrashReporting() ) return .none + + case .updateStateAfterConfigUpdate(let walletConfig): + state.walletConfig = walletConfig + state.onboardingState.walletConfig = walletConfig + return .none case .dismissAlert: state.alert = nil return .none - case let .debug(.updateFlag(flag, isEnabled)): - return walletConfigProvider.update(flag, !isEnabled) - .receive(on: mainQueue) - .map { _ in return Action.debug(.flagUpdated) } - .eraseToEffect() - .cancellable(id: WalletConfigCancelId.self, cancelInFlight: true) - - case .debug(.flagUpdated): - return walletConfigProvider.load() - .receive(on: mainQueue) - .map { Action.debug(.walletConfigLoaded($0)) } - .eraseToEffect() - .cancellable(id: WalletConfigCancelId.self, cancelInFlight: true) - - case let .debug(.walletConfigLoaded(walletConfig)): - updateStateAfterConfigUpdate(state: &state, config: walletConfig) + case .home, .destination, .onboarding, .phraseDisplay, .phraseValidation, .sandbox, + .welcome, .binding, .debug: return .none } } } - - private func updateStateAfterConfigUpdate(state: inout RootReducer.State, config: WalletConfig) { - state.walletConfig = config - state.onboardingState.walletConfig = config - } } diff --git a/secant/Features/Root/RootStore.swift b/secant/Features/Root/RootStore.swift index 8532daf..d475f87 100644 --- a/secant/Features/Root/RootStore.swift +++ b/secant/Features/Root/RootStore.swift @@ -12,6 +12,7 @@ struct RootReducer: ReducerProtocol { struct State: Equatable { @BindingState var alert: AlertState? var appInitializationState: InitializationState = .uninitialized + var debugState: DebugState var destinationState: DestinationState var homeState: HomeReducer.State var onboardingState: OnboardingFlowReducer.State @@ -36,6 +37,7 @@ struct RootReducer: ReducerProtocol { case phraseDisplay(RecoveryPhraseDisplayReducer.Action) case phraseValidation(RecoveryPhraseValidationFlowReducer.Action) case sandbox(SandboxReducer.Action) + case updateStateAfterConfigUpdate(WalletConfig) case walletConfigLoaded(WalletConfig) case welcome(WelcomeReducer.Action) } @@ -83,6 +85,8 @@ struct RootReducer: ReducerProtocol { initializationReduce() destinationReduce() + + debugReduce() } } @@ -172,6 +176,7 @@ extension RootReducer { extension RootReducer.State { static var placeholder: Self { .init( + debugState: .placeholder, destinationState: .placeholder, homeState: .placeholder, onboardingState: .init( diff --git a/secant/Features/Root/RootView.swift b/secant/Features/Root/RootView.swift index 3767274..ec5de1d 100644 --- a/secant/Features/Root/RootView.swift +++ b/secant/Features/Root/RootView.swift @@ -126,6 +126,14 @@ private extension RootView { viewStore.goToDestination(.welcome) } + Button("Test Crash Reporter") { + viewStore.send(.debug(.testCrashReporter)) + } + + Button("Rescan Blockchain") { + viewStore.send(.debug(.rescanBlockchain)) + } + Button("[Be careful] Nuke Wallet") { viewStore.send(.initialization(.nukeWalletRequest)) } @@ -156,6 +164,10 @@ private extension RootView { } } .alert(self.store.scope(state: \.destinationState.alert), dismiss: .destination(.dismissAlert)) + .confirmationDialog( + store.scope(state: \.debugState.rescanDialog), + dismiss: .debug(.cancelRescan) + ) } .navigationBarTitle("Startup") } diff --git a/secant/Features/Settings/SettingsStore.swift b/secant/Features/Settings/SettingsStore.swift index cb88481..93e44a9 100644 --- a/secant/Features/Settings/SettingsStore.swift +++ b/secant/Features/Settings/SettingsStore.swift @@ -17,7 +17,6 @@ struct SettingsReducer: ReducerProtocol { @BindingState var isCrashReportingOn: Bool var isSharingLogs = false var phraseDisplayState: RecoveryPhraseDisplayReducer.State - var rescanDialog: ConfirmationDialogState? var supportData: SupportData? var tempSDKDir: URL { @@ -43,20 +42,15 @@ struct SettingsReducer: ReducerProtocol { case backupWallet case backupWalletAccessRequest case binding(BindingAction) - case cancelRescan case dismissAlert case exportLogs - case fullRescan case logsExported case logsExportFailed(String) case logsShareFinished case onAppear case phraseDisplay(RecoveryPhraseDisplayReducer.Action) - case quickRescan - case rescanBlockchain case sendSupportMail case sendSupportMailFinished - case testCrashReporter // this will crash the app if live. case updateDestination(SettingsReducer.State.Destination?) } @@ -108,10 +102,6 @@ struct SettingsReducer: ReducerProtocol { return .run { [state] _ in await userStoredPreferences.setIsUserOptedOutOfCrashReporting(state.isCrashReportingOn) } - - case .cancelRescan, .quickRescan, .fullRescan: - state.rescanDialog = nil - return .none case .dismissAlert: state.alert = nil @@ -146,18 +136,6 @@ struct SettingsReducer: ReducerProtocol { state.isSharingLogs = false return .none - case .rescanBlockchain: - state.rescanDialog = .init( - title: TextState("Rescan"), - message: TextState("Select the rescan you want"), - buttons: [ - .default(TextState("Quick rescan"), action: .send(.quickRescan)), - .default(TextState("Full rescan"), action: .send(.fullRescan)), - .cancel(TextState("Cancel")) - ] - ) - return .none - case .phraseDisplay: state.destination = nil return .none @@ -166,10 +144,6 @@ struct SettingsReducer: ReducerProtocol { state.destination = destination return .none - case .testCrashReporter: - crashReporter.testCrash() - return .none - case .binding: return .none diff --git a/secant/Features/Settings/SettingsView.swift b/secant/Features/Settings/SettingsView.swift index d0f0340..245a879 100644 --- a/secant/Features/Settings/SettingsView.swift +++ b/secant/Features/Settings/SettingsView.swift @@ -18,13 +18,6 @@ struct SettingsView: View { .activeButtonStyle .frame(height: 50) - Button( - action: { viewStore.send(.rescanBlockchain) }, - label: { Text("Rescan Blockchain") } - ) - .primaryButtonStyle - .frame(height: 50) - Button( action: { viewStore.send(.exportLogs) }, label: { @@ -42,13 +35,6 @@ struct SettingsView: View { .frame(height: 50) .disabled(viewStore.exportLogsDisabled) - Button( - action: { viewStore.send(.testCrashReporter) }, - label: { Text("Test Crash Reporter") } - ) - .primaryButtonStyle - .frame(height: 50) - Button( action: { viewStore.send(.sendSupportMail) }, label: { Text("Send us feedback!") } @@ -61,10 +47,6 @@ struct SettingsView: View { .padding(.horizontal, 30) .navigationTitle("Settings") .applyScreenBackground() - .confirmationDialog( - store.scope(state: \.rescanDialog), - dismiss: .cancelRescan - ) .navigationLinkEmpty( isActive: viewStore.bindingForBackupPhrase, destination: { diff --git a/secantTests/HomeTests/HomeTests.swift b/secantTests/HomeTests/HomeTests.swift index 7a8490c..6c2b954 100644 --- a/secantTests/HomeTests/HomeTests.swift +++ b/secantTests/HomeTests/HomeTests.swift @@ -87,54 +87,4 @@ class HomeTests: XCTestCase { // the .onDisappear action cancles the observer of the synchronizer status change. store.send(.onDisappear) } - - @MainActor func testQuickRescan_ResetToHomeScreen() async throws { - let homeState = HomeReducer.State( - balanceBreakdownState: .placeholder, - destination: .profile, - profileState: .placeholder, - requestState: .placeholder, - scanState: .placeholder, - sendState: .placeholder, - shieldedBalance: Balance.zero, - synchronizerStatusSnapshot: .default, - walletEventsState: .emptyPlaceHolder - ) - - let store = TestStore( - initialState: homeState, - reducer: HomeReducer() - ) - - await store.send(.profile(.settings(.quickRescan))) { state in - state.destination = nil - } - - await store.receive(.rewindDone(nil, .quickRescan)) - } - - @MainActor func testFullRescan_ResetToHomeScreen() async throws { - let homeState = HomeReducer.State( - balanceBreakdownState: .placeholder, - destination: .profile, - profileState: .placeholder, - requestState: .placeholder, - scanState: .placeholder, - sendState: .placeholder, - shieldedBalance: Balance.zero, - synchronizerStatusSnapshot: .default, - walletEventsState: .emptyPlaceHolder - ) - - let store = TestStore( - initialState: homeState, - reducer: HomeReducer() - ) - - await store.send(.profile(.settings(.fullRescan))) { state in - state.destination = nil - } - - await store.receive(.rewindDone(nil, .fullRescan)) - } } diff --git a/secantTests/ProfileTests/ProfileTests.swift b/secantTests/ProfileTests/ProfileTests.swift index 05b4066..99f345f 100644 --- a/secantTests/ProfileTests/ProfileTests.swift +++ b/secantTests/ProfileTests/ProfileTests.swift @@ -11,6 +11,9 @@ import ComposableArchitecture import ZcashLightClientKit class ProfileTests: XCTestCase { + // swiftlint:disable line_length + let uAddressEncoding = "utest1zkkkjfxkamagznjr6ayemffj2d2gacdwpzcyw669pvg06xevzqslpmm27zjsctlkstl2vsw62xrjktmzqcu4yu9zdhdxqz3kafa4j2q85y6mv74rzjcgjg8c0ytrg7dwyzwtgnuc76h" + @MainActor func testSynchronizerStateChanged_AnyButSynced() async throws { let store = TestStore( initialState: .placeholder, @@ -20,9 +23,8 @@ class ProfileTests: XCTestCase { dependencies.sdkSynchronizer = SDKSynchronizerDependency.mock } - // swiftlint:disable line_length let uAddress = try UnifiedAddress( - encoding: "utest1zkkkjfxkamagznjr6ayemffj2d2gacdwpzcyw669pvg06xevzqslpmm27zjsctlkstl2vsw62xrjktmzqcu4yu9zdhdxqz3kafa4j2q85y6mv74rzjcgjg8c0ytrg7dwyzwtgnuc76h", + encoding: uAddressEncoding, network: .testnet ) @@ -33,4 +35,26 @@ class ProfileTests: XCTestCase { state.sdkVersion = "0.18.1-beta" } } + + func testCopyUnifiedAddressToPasteboard() throws { + let testPasteboard = PasteboardClient.testPasteboard + let uAddress = try UnifiedAddress(encoding: uAddressEncoding, network: .testnet) + + let store = TestStore( + initialState: ProfileReducer.State( + addressDetailsState: AddressDetailsReducer.State(uAddress: uAddress) + ), + reducer: ProfileReducer() + ) { + $0.pasteboard = testPasteboard + } + + store.send(.copyUnifiedAddressToPastboard) + + XCTAssertEqual( + testPasteboard.getString()?.data, + uAddress.stringEncoded, + "AddressDetails: `testCopyUnifiedAddressToPasteboard` is expected to match the input `\(uAddress.stringEncoded)`" + ) + } } diff --git a/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationFlowFeatureFlagTests.swift b/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationFlowFeatureFlagTests.swift index 8608590..6bd02ff 100644 --- a/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationFlowFeatureFlagTests.swift +++ b/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationFlowFeatureFlagTests.swift @@ -161,6 +161,7 @@ class RecoveryPhraseValidationFlowFeatureFlagTests: XCTestCase { ) let appState = RootReducer.State( + debugState: .placeholder, destinationState: .placeholder, homeState: .placeholder, onboardingState: .init( diff --git a/secantTests/RootTests/AppInitializationTests.swift b/secantTests/RootTests/AppInitializationTests.swift index c6cae55..32b9f4c 100644 --- a/secantTests/RootTests/AppInitializationTests.swift +++ b/secantTests/RootTests/AppInitializationTests.swift @@ -68,6 +68,7 @@ class AppInitializationTests: XCTestCase { let walletConfig = WalletConfig(flags: defaultRawFlags) let appState = RootReducer.State( + debugState: .placeholder, destinationState: .placeholder, homeState: .placeholder, onboardingState: .init( diff --git a/secantTests/RootTests/DebugTests.swift b/secantTests/RootTests/DebugTests.swift new file mode 100644 index 0000000..883f9d5 --- /dev/null +++ b/secantTests/RootTests/DebugTests.swift @@ -0,0 +1,107 @@ +// +// DebugTests.swift +// secantTests +// +// Created by Lukáš Korba on 02.03.2023. +// + +import XCTest +@testable import secant_testnet +import ComposableArchitecture + +@MainActor +class DebugTests: XCTestCase { + func testRescanBlockchain() async throws { + let store = TestStore( + initialState: .placeholder, + reducer: RootReducer() + ) + + await store.send(.debug(.rescanBlockchain)) { state in + state.debugState.rescanDialog = .init( + title: TextState("Rescan"), + message: TextState("Select the rescan you want"), + buttons: [ + .default(TextState("Quick rescan"), action: .send(.debug(.quickRescan))), + .default(TextState("Full rescan"), action: .send(.debug(.fullRescan))), + .cancel(TextState("Cancel")) + ] + ) + } + } + + func testRescanBlockchain_Cancelling() async throws { + var mockState = RootReducer.State.placeholder + + mockState.debugState.rescanDialog = .init( + title: TextState("Rescan"), + message: TextState("Select the rescan you want"), + buttons: [ + .default(TextState("Quick rescan"), action: .send(.debug(.quickRescan))), + .default(TextState("Full rescan"), action: .send(.debug(.fullRescan))), + .cancel(TextState("Cancel")) + ] + ) + + let store = TestStore( + initialState: mockState, + reducer: RootReducer() + ) + + await store.send(.debug(.cancelRescan)) { state in + state.debugState.rescanDialog = nil + } + } + + func testRescanBlockchain_QuickRescanClearance() async throws { + var mockState = RootReducer.State.placeholder + + mockState.debugState.rescanDialog = .init( + title: TextState("Rescan"), + message: TextState("Select the rescan you want"), + buttons: [ + .default(TextState("Quick rescan"), action: .send(.debug(.quickRescan))), + .default(TextState("Full rescan"), action: .send(.debug(.fullRescan))), + .cancel(TextState("Cancel")) + ] + ) + + let store = TestStore( + initialState: mockState, + reducer: RootReducer() + ) + + await store.send(.debug(.quickRescan)) { state in + state.destinationState.internalDestination = .home + state.destinationState.previousDestination = .welcome + } + + await store.receive(.debug(.rewindDone(nil, .debug(.quickRescan)))) + } + + func testRescanBlockchain_FullRescanClearance() async throws { + var mockState = RootReducer.State.placeholder + + mockState.debugState.rescanDialog = .init( + title: TextState("Rescan"), + message: TextState("Select the rescan you want"), + buttons: [ + .default(TextState("Quick rescan"), action: .send(.debug(.quickRescan))), + .default(TextState("Full rescan"), action: .send(.debug(.fullRescan))), + .cancel(TextState("Cancel")) + ] + ) + + let store = TestStore( + initialState: mockState, + reducer: RootReducer() + ) + + await store.send(.debug(.fullRescan)) { state in + state.destinationState.internalDestination = .home + state.destinationState.previousDestination = .welcome + } + + await store.receive(.debug(.rewindDone(nil, .debug(.fullRescan)))) + } +} diff --git a/secantTests/SettingsTests/SettingsTests.swift b/secantTests/SettingsTests/SettingsTests.swift index 9b66d20..1bf42bf 100644 --- a/secantTests/SettingsTests/SettingsTests.swift +++ b/secantTests/SettingsTests/SettingsTests.swift @@ -80,112 +80,12 @@ class SettingsTests: XCTestCase { await store.finish() } - func testRescanBlockchain() async throws { - let store = TestStore( - initialState: .placeholder, - reducer: SettingsReducer() - ) - - await store.send(.rescanBlockchain) { state in - state.rescanDialog = .init( - title: TextState("Rescan"), - message: TextState("Select the rescan you want"), - buttons: [ - .default(TextState("Quick rescan"), action: .send(.quickRescan)), - .default(TextState("Full rescan"), action: .send(.fullRescan)), - .cancel(TextState("Cancel")) - ] - ) - } - } - - func testRescanBlockchain_Cancelling() async throws { - let store = TestStore( - initialState: SettingsReducer.State( - destination: nil, - isCrashReportingOn: false, - phraseDisplayState: .init(), - rescanDialog: .init( - title: TextState("Rescan"), - message: TextState("Select the rescan you want"), - buttons: [ - .default(TextState("Quick rescan"), action: .send(.quickRescan)), - .default(TextState("Full rescan"), action: .send(.fullRescan)), - .cancel(TextState("Cancel")) - ] - ) - ), - reducer: SettingsReducer() - ) - - await store.send(.cancelRescan) { state in - state.rescanDialog = nil - } - } - - func testRescanBlockchain_QuickRescanClearance() async throws { - let store = TestStore( - initialState: SettingsReducer.State( - destination: nil, - isCrashReportingOn: false, - phraseDisplayState: .init(), - rescanDialog: .init( - title: TextState("Rescan"), - message: TextState("Select the rescan you want"), - buttons: [ - .default(TextState("Quick rescan"), action: .send(.quickRescan)), - .default(TextState("Full rescan"), action: .send(.fullRescan)), - .cancel(TextState("Cancel")) - ] - ) - ), - reducer: SettingsReducer() - ) - - await store.send(.quickRescan) { state in - state.rescanDialog = nil - } - } - - func testRescanBlockchain_FullRescanClearance() async throws { - let store = TestStore( - initialState: SettingsReducer.State( - destination: nil, - isCrashReportingOn: false, - phraseDisplayState: .init(), - rescanDialog: .init( - title: TextState("Rescan"), - message: TextState("Select the rescan you want"), - buttons: [ - .default(TextState("Quick rescan"), action: .send(.quickRescan)), - .default(TextState("Full rescan"), action: .send(.fullRescan)), - .cancel(TextState("Cancel")) - ] - ) - ), - reducer: SettingsReducer() - ) - - await store.send(.fullRescan) { state in - state.rescanDialog = nil - } - } - func testExportLogs_ButtonDisableShareEnable() async throws { let store = TestStore( initialState: SettingsReducer.State( destination: nil, isCrashReportingOn: false, - phraseDisplayState: .init(), - rescanDialog: .init( - title: TextState("Rescan"), - message: TextState("Select the rescan you want"), - buttons: [ - .default(TextState("Quick rescan"), action: .send(.quickRescan)), - .default(TextState("Full rescan"), action: .send(.fullRescan)), - .cancel(TextState("Cancel")) - ] - ) + phraseDisplayState: .init() ), reducer: SettingsReducer() ) @@ -208,16 +108,7 @@ class SettingsTests: XCTestCase { destination: nil, isCrashReportingOn: false, isSharingLogs: true, - phraseDisplayState: .init(), - rescanDialog: .init( - title: TextState("Rescan"), - message: TextState("Select the rescan you want"), - buttons: [ - .default(TextState("Quick rescan"), action: .send(.quickRescan)), - .default(TextState("Full rescan"), action: .send(.fullRescan)), - .cancel(TextState("Cancel")) - ] - ) + phraseDisplayState: .init() ), reducer: SettingsReducer() ) diff --git a/secantTests/SnapshotTests/HomeSnapshotTests/HomeSnapshotTests.swift b/secantTests/SnapshotTests/HomeSnapshotTests/HomeSnapshotTests.swift index 0986918..6532720 100644 --- a/secantTests/SnapshotTests/HomeSnapshotTests/HomeSnapshotTests.swift +++ b/secantTests/SnapshotTests/HomeSnapshotTests/HomeSnapshotTests.swift @@ -39,9 +39,9 @@ class HomeSnapshotTests: XCTestCase { initialState: .init( balanceBreakdownState: .placeholder, profileState: .placeholder, - requestState: .placeholder, scanState: .placeholder, sendState: .placeholder, + settingsState: .placeholder, shieldedBalance: balance.redacted, synchronizerStatusSnapshot: .default, walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: walletEvents)) diff --git a/secantTests/SnapshotTests/WalletEventsSnapshotTests/WalletEventsSnapshotTests.swift b/secantTests/SnapshotTests/WalletEventsSnapshotTests/WalletEventsSnapshotTests.swift index bae790e..2f634d9 100644 --- a/secantTests/SnapshotTests/WalletEventsSnapshotTests/WalletEventsSnapshotTests.swift +++ b/secantTests/SnapshotTests/WalletEventsSnapshotTests/WalletEventsSnapshotTests.swift @@ -54,9 +54,9 @@ class WalletEventsSnapshotTests: XCTestCase { initialState: .init( balanceBreakdownState: .placeholder, profileState: .placeholder, - requestState: .placeholder, scanState: .placeholder, sendState: .placeholder, + settingsState: .placeholder, shieldedBalance: balance.redacted, synchronizerStatusSnapshot: .default, walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent])) @@ -106,9 +106,9 @@ class WalletEventsSnapshotTests: XCTestCase { initialState: .init( balanceBreakdownState: .placeholder, profileState: .placeholder, - requestState: .placeholder, scanState: .placeholder, sendState: .placeholder, + settingsState: .placeholder, shieldedBalance: balance.redacted, synchronizerStatusSnapshot: .default, walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent])) @@ -158,9 +158,9 @@ class WalletEventsSnapshotTests: XCTestCase { initialState: .init( balanceBreakdownState: .placeholder, profileState: .placeholder, - requestState: .placeholder, scanState: .placeholder, sendState: .placeholder, + settingsState: .placeholder, shieldedBalance: balance.redacted, synchronizerStatusSnapshot: .default, walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent])) @@ -216,9 +216,9 @@ class WalletEventsSnapshotTests: XCTestCase { initialState: .init( balanceBreakdownState: .placeholder, profileState: .placeholder, - requestState: .placeholder, scanState: .placeholder, sendState: .placeholder, + settingsState: .placeholder, shieldedBalance: balance.redacted, synchronizerStatusSnapshot: .default, walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent])) diff --git a/secantTests/WalletConfigProviderTests/WalletConfigProviderTests.swift b/secantTests/WalletConfigProviderTests/WalletConfigProviderTests.swift index caf8a12..8a36af1 100644 --- a/secantTests/WalletConfigProviderTests/WalletConfigProviderTests.swift +++ b/secantTests/WalletConfigProviderTests/WalletConfigProviderTests.swift @@ -127,8 +127,10 @@ class WalletConfigProviderTests: XCTestCase { defaultRawFlags[.onboardingFlow] = true let flags = WalletConfig(flags: defaultRawFlags) + store.send(.debug(.walletConfigLoaded(flags))) + // The new flag's value has to be propagated to all `walletConfig` instances - store.send(.debug(.walletConfigLoaded(flags))) { state in + store.receive(.updateStateAfterConfigUpdate(flags)) { state in state.walletConfig = flags state.onboardingState.walletConfig = flags }