From 56e1364659e18185a0c9516947c0c71b0844c015 Mon Sep 17 00:00:00 2001 From: Michal Fousek Date: Mon, 27 Feb 2023 13:55:47 +0100 Subject: [PATCH] [#554] Use WalletConfigProvider and WalletConfig in the TCA (#582) Closes #554 - WalletConfigProvider and WalletConfig are used in the Secant app. - First feature flag (`onboarding`) is in use. Co-authored-by: Lukas Korba --- secant.xcodeproj/project.pbxproj | 64 ++++---- ...gerLive.swift => FileManagerLiveKey.swift} | 2 +- ...gerTest.swift => FileManagerTestKey.swift} | 2 +- .../UserDefaultsWalletConfigStorage.swift | 0 .../WalletConfigProvider.swift | 0 .../WalletConfigProviderInterface.swift | 0 .../WalletConfigProviderLiveKey.swift | 0 .../WalletConfigProviderTestKey.swift | 3 +- .../OnboardingFlow/OnboardingFlowStore.swift | 22 ++- .../OnboardingFlow/OnboardingFlowView.swift | 60 ++++---- .../Views/OnboardingContentView.swift | 6 +- .../Views/OnboardingFooterView.swift | 5 +- .../Views/OnboardingHeaderView.swift | 9 +- secant/Features/Root/RootDestination.swift | 10 +- secant/Features/Root/RootInitialization.swift | 26 +++- secant/Features/Root/RootStore.swift | 4 + secant/Models/WalletConfig.swift | 5 +- secantTests/DeeplinkTests/DeeplinkTests.swift | 21 +-- secantTests/HomeTests/HomeTests.swift | 4 +- .../OnboardingFlowFeatureFlagTests.swift | 64 ++++++++ .../OnboardingStoreTests.swift | 17 ++- secantTests/ProfileTests/ProfileTests.swift | 2 +- .../RootTests/AppInitializationTests.swift | 140 ++++++++---------- secantTests/SettingsTests/SettingsTests.swift | 16 +- .../AddressDetailsSnapshotTests.swift | 1 + .../OnboardingSnapshotTests.swift | 5 +- .../WalletConfigProviderTests.swift | 0 27 files changed, 300 insertions(+), 188 deletions(-) rename secant/Dependencies/FileManager/{FileManagerLive.swift => FileManagerLiveKey.swift} (95%) rename secant/Dependencies/FileManager/{FileManagerTest.swift => FileManagerTestKey.swift} (93%) rename secant/Dependencies/{FeatureFlagsManager => WalletConfigProvider}/UserDefaultsWalletConfigStorage.swift (100%) rename secant/Dependencies/{FeatureFlagsManager => WalletConfigProvider}/WalletConfigProvider.swift (100%) rename secant/Dependencies/{FeatureFlagsManager => WalletConfigProvider}/WalletConfigProviderInterface.swift (100%) rename secant/Dependencies/{FeatureFlagsManager => WalletConfigProvider}/WalletConfigProviderLiveKey.swift (100%) rename secant/Dependencies/{FeatureFlagsManager => WalletConfigProvider}/WalletConfigProviderTestKey.swift (89%) create mode 100644 secantTests/OnboardingTests/OnboardingFlowFeatureFlagTests.swift rename secantTests/{WalletConfigProvider => WalletConfigProviderTests}/WalletConfigProviderTests.swift (100%) diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index d3ec4243..348b53f3 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -84,10 +84,10 @@ 0D26AEDC299E8196005260EE /* CheckCircle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346D41E328DF0B8600963F36 /* CheckCircle.swift */; }; 0D26AEDD299E8196005260EE /* LogStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0F5744297EBA1B005304FA /* LogStore.swift */; }; 0D26AEDE299E8196005260EE /* RecoveryPhraseRandomizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911422848EEB90073DD9A /* RecoveryPhraseRandomizer.swift */; }; - 0D26AEDF299E8196005260EE /* FileManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C42923C8AF003D0F8B /* FileManagerTest.swift */; }; + 0D26AEDF299E8196005260EE /* FileManagerTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C42923C8AF003D0F8B /* FileManagerTestKey.swift */; }; 0D26AEE0299E8196005260EE /* SecItemLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863BE2923C72C003D0F8B /* SecItemLive.swift */; }; 0D26AEE1299E8196005260EE /* CircularFrameBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 669FDAEA272C23C2007B9422 /* CircularFrameBadge.swift */; }; - 0D26AEE2299E8196005260EE /* FileManagerLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863B82923C6D7003D0F8B /* FileManagerLive.swift */; }; + 0D26AEE2299E8196005260EE /* FileManagerLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863B82923C6D7003D0F8B /* FileManagerLiveKey.swift */; }; 0D26AEE3299E8196005260EE /* AppVersionTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF979291F7EB0000A1A05 /* AppVersionTestKey.swift */; }; 0D26AEE4299E8196005260EE /* CurrencySelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8719CC27FB0D3B0082C926 /* CurrencySelectionView.swift */; }; 0D26AEE5299E8196005260EE /* RecoveryPhraseRandomizerTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863A429239DCB003D0F8B /* RecoveryPhraseRandomizerTestKey.swift */; }; @@ -320,12 +320,15 @@ 3448CB3228E47666006ADEDB /* NotEnoughFreeSpaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448CB3128E47666006ADEDB /* NotEnoughFreeSpaceView.swift */; }; 3448CB3728E485CB006ADEDB /* NotEnoughFeeSpaceSnapshots.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448CB3628E485CB006ADEDB /* NotEnoughFeeSpaceSnapshots.swift */; }; 346715A528E2027D0035F7C4 /* CheckCircleStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346715A428E2027D0035F7C4 /* CheckCircleStore.swift */; }; + 346715A828E20FE40035F7C4 /* TransactionConfirmationSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346715A728E20FE40035F7C4 /* TransactionConfirmationSnapshotTests.swift */; }; + 3469F18229ACD70500A07146 /* OnboardingFlowFeatureFlagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3469F18129ACD70500A07146 /* OnboardingFlowFeatureFlagTests.swift */; }; 346D41E428DF0B8600963F36 /* CheckCircle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346D41E328DF0B8600963F36 /* CheckCircle.swift */; }; 34BF09092927C98000222134 /* Memo+toString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34BF09082927C98000222134 /* Memo+toString.swift */; }; 34DA414728E4385800F8CC61 /* TransactionSendingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34DA414628E4385800F8CC61 /* TransactionSendingView.swift */; }; 34DA414928E439CD00F8CC61 /* sendingTransaction.json in Resources */ = {isa = PBXBuildFile; fileRef = 34DA414828E439CD00F8CC61 /* sendingTransaction.json */; }; 34E0AF1128DEE5220034CF37 /* Wedge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E0AF1028DEE5220034CF37 /* Wedge.swift */; }; 34E5F2F328E46DB700C17E5F /* DiskSpaceChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E5F2F228E46DB700C17E5F /* DiskSpaceChecker.swift */; }; + 34F039B329ABCE8500CF0053 /* WalletConfigProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F039B229ABCE8500CF0053 /* WalletConfigProviderTests.swift */; }; 34F682E529A75EB60022C079 /* WalletConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F682E429A75EB60022C079 /* WalletConfig.swift */; }; 34F682E629A75EB60022C079 /* WalletConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F682E429A75EB60022C079 /* WalletConfig.swift */; }; 34F682EC29A763FD0022C079 /* WalletConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F682EB29A763FD0022C079 /* WalletConfigProvider.swift */; }; @@ -338,7 +341,6 @@ 34F682F629A7641B0022C079 /* WalletConfigProviderTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F682F429A7641B0022C079 /* WalletConfigProviderTestKey.swift */; }; 34F682F829A775C10022C079 /* UserDefaultsWalletConfigStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F682F729A775C10022C079 /* UserDefaultsWalletConfigStorage.swift */; }; 34F682F929A775C10022C079 /* UserDefaultsWalletConfigStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F682F729A775C10022C079 /* UserDefaultsWalletConfigStorage.swift */; }; - 34F682FC29A784660022C079 /* WalletConfigProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F682FB29A784660022C079 /* WalletConfigProviderTests.swift */; }; 660558E9270C7A54009D6954 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 660558E8270C7A54009D6954 /* Colors.xcassets */; }; 660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660558F5270C862F009D6954 /* Fonts+Generated.swift */; }; 660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660558F6270C862F009D6954 /* XCAssets+Generated.swift */; }; @@ -475,13 +477,13 @@ 9EB863A829239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863A529239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift */; }; 9EB863A929239DCB003D0F8B /* RecoveryPhraseRandomizerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863A629239DCB003D0F8B /* RecoveryPhraseRandomizerInterface.swift */; }; 9EB863AA29239EB2003D0F8B /* RecoveryPhraseRandomizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911422848EEB90073DD9A /* RecoveryPhraseRandomizer.swift */; }; - 9EB863B92923C6D7003D0F8B /* FileManagerLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863B82923C6D7003D0F8B /* FileManagerLive.swift */; }; + 9EB863B92923C6D7003D0F8B /* FileManagerLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863B82923C6D7003D0F8B /* FileManagerLiveKey.swift */; }; 9EB863BB2923C6F8003D0F8B /* NotificationCenterLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863BA2923C6F8003D0F8B /* NotificationCenterLive.swift */; }; 9EB863BD2923C704003D0F8B /* NotificationCenterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863BC2923C704003D0F8B /* NotificationCenterTest.swift */; }; 9EB863BF2923C72C003D0F8B /* SecItemLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863BE2923C72C003D0F8B /* SecItemLive.swift */; }; 9EB863C12923C779003D0F8B /* URIParserLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C02923C779003D0F8B /* URIParserLive.swift */; }; 9EB863C32923C807003D0F8B /* URIParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C22923C807003D0F8B /* URIParserTest.swift */; }; - 9EB863C52923C8AF003D0F8B /* FileManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C42923C8AF003D0F8B /* FileManagerTest.swift */; }; + 9EB863C52923C8AF003D0F8B /* FileManagerTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C42923C8AF003D0F8B /* FileManagerTestKey.swift */; }; 9EB863C72923C93B003D0F8B /* UserPreferencesStorageLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C62923C93B003D0F8B /* UserPreferencesStorageLive.swift */; }; 9EB863C92923C953003D0F8B /* UserPreferencesStorageMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C82923C953003D0F8B /* UserPreferencesStorageMocks.swift */; }; 9EB863CB2923CA20003D0F8B /* SDKSynchronizerLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863CA2923CA20003D0F8B /* SDKSynchronizerLive.swift */; }; @@ -644,19 +646,21 @@ 3448CB3128E47666006ADEDB /* NotEnoughFreeSpaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotEnoughFreeSpaceView.swift; sourceTree = ""; }; 3448CB3628E485CB006ADEDB /* NotEnoughFeeSpaceSnapshots.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotEnoughFeeSpaceSnapshots.swift; sourceTree = ""; }; 346715A428E2027D0035F7C4 /* CheckCircleStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckCircleStore.swift; sourceTree = ""; }; + 346715A728E20FE40035F7C4 /* TransactionConfirmationSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionConfirmationSnapshotTests.swift; sourceTree = ""; }; + 3469F18129ACD70500A07146 /* OnboardingFlowFeatureFlagTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFlowFeatureFlagTests.swift; sourceTree = ""; }; 346D41E328DF0B8600963F36 /* CheckCircle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckCircle.swift; sourceTree = ""; }; 34BF09082927C98000222134 /* Memo+toString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Memo+toString.swift"; sourceTree = ""; }; 34DA414628E4385800F8CC61 /* TransactionSendingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionSendingView.swift; sourceTree = ""; }; 34DA414828E439CD00F8CC61 /* sendingTransaction.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = sendingTransaction.json; sourceTree = ""; }; 34E0AF1028DEE5220034CF37 /* Wedge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wedge.swift; sourceTree = ""; }; 34E5F2F228E46DB700C17E5F /* DiskSpaceChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskSpaceChecker.swift; sourceTree = ""; }; + 34F039B229ABCE8500CF0053 /* WalletConfigProviderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletConfigProviderTests.swift; sourceTree = ""; }; 34F682E429A75EB60022C079 /* WalletConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConfig.swift; sourceTree = ""; }; 34F682EB29A763FD0022C079 /* WalletConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConfigProvider.swift; sourceTree = ""; }; 34F682EE29A7640A0022C079 /* WalletConfigProviderInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConfigProviderInterface.swift; sourceTree = ""; }; 34F682F129A764120022C079 /* WalletConfigProviderLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConfigProviderLiveKey.swift; sourceTree = ""; }; 34F682F429A7641B0022C079 /* WalletConfigProviderTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConfigProviderTestKey.swift; sourceTree = ""; }; 34F682F729A775C10022C079 /* UserDefaultsWalletConfigStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsWalletConfigStorage.swift; sourceTree = ""; }; - 34F682FB29A784660022C079 /* WalletConfigProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConfigProviderTests.swift; sourceTree = ""; }; 660558E8270C7A54009D6954 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; 660558F5270C862F009D6954 /* Fonts+Generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Fonts+Generated.swift"; sourceTree = ""; }; 660558F6270C862F009D6954 /* XCAssets+Generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCAssets+Generated.swift"; sourceTree = ""; }; @@ -786,13 +790,13 @@ 9EB863A429239DCB003D0F8B /* RecoveryPhraseRandomizerTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizerTestKey.swift; sourceTree = ""; }; 9EB863A529239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizerLiveKey.swift; sourceTree = ""; }; 9EB863A629239DCB003D0F8B /* RecoveryPhraseRandomizerInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizerInterface.swift; sourceTree = ""; }; - 9EB863B82923C6D7003D0F8B /* FileManagerLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerLive.swift; sourceTree = ""; }; + 9EB863B82923C6D7003D0F8B /* FileManagerLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerLiveKey.swift; sourceTree = ""; }; 9EB863BA2923C6F8003D0F8B /* NotificationCenterLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterLive.swift; sourceTree = ""; }; 9EB863BC2923C704003D0F8B /* NotificationCenterTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterTest.swift; sourceTree = ""; }; 9EB863BE2923C72C003D0F8B /* SecItemLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecItemLive.swift; sourceTree = ""; }; 9EB863C02923C779003D0F8B /* URIParserLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIParserLive.swift; sourceTree = ""; }; 9EB863C22923C807003D0F8B /* URIParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIParserTest.swift; sourceTree = ""; }; - 9EB863C42923C8AF003D0F8B /* FileManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerTest.swift; sourceTree = ""; }; + 9EB863C42923C8AF003D0F8B /* FileManagerTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerTestKey.swift; sourceTree = ""; }; 9EB863C62923C93B003D0F8B /* UserPreferencesStorageLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageLive.swift; sourceTree = ""; }; 9EB863C82923C953003D0F8B /* UserPreferencesStorageMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageMocks.swift; sourceTree = ""; }; 9EB863CA2923CA20003D0F8B /* SDKSynchronizerLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerLive.swift; sourceTree = ""; }; @@ -1001,7 +1005,6 @@ 0DFE93DD272C6D4B000FCCA5 /* BackupFlowTests */, 9E94C61E28AA7DD5008256E9 /* BalanceBreakdownTests */, 9EAB4674285B5C68002904A0 /* DeeplinkTests */, - 34F682FA29A784580022C079 /* WalletConfigProvider */, 9E3911372848AD3A0073DD9A /* HomeTests */, 9E391122283E4C970073DD9A /* ImportWalletTests */, 9E6713EF2897F80A00A6796F /* MultiLineTextFieldTests */, @@ -1015,6 +1018,7 @@ 9E66129C2889388C00C75B70 /* SettingsTests */, 9E391162284E3ECF0073DD9A /* SnapshotTests */, 9EF8135927ECC25E0075AF48 /* UtilTests */, + 34F039B129ABCE8500CF0053 /* WalletConfigProviderTests */, 9E5BF63D281953F900BA3F17 /* WalletEventsTests */, ); path = secantTests; @@ -1180,7 +1184,15 @@ path = CheckCircle; sourceTree = ""; }; - 34F682EA29A763F00022C079 /* FeatureFlagsManager */ = { + 34F039B129ABCE8500CF0053 /* WalletConfigProviderTests */ = { + isa = PBXGroup; + children = ( + 34F039B229ABCE8500CF0053 /* WalletConfigProviderTests.swift */, + ); + path = WalletConfigProviderTests; + sourceTree = ""; + }; + 34F682EA29A763F00022C079 /* WalletConfigProvider */ = { isa = PBXGroup; children = ( 34F682EB29A763FD0022C079 /* WalletConfigProvider.swift */, @@ -1189,14 +1201,6 @@ 34F682F129A764120022C079 /* WalletConfigProviderLiveKey.swift */, 34F682F429A7641B0022C079 /* WalletConfigProviderTestKey.swift */, ); - path = FeatureFlagsManager; - sourceTree = ""; - }; - 34F682FA29A784580022C079 /* WalletConfigProvider */ = { - isa = PBXGroup; - children = ( - 34F682FB29A784660022C079 /* WalletConfigProviderTests.swift */, - ); path = WalletConfigProvider; sourceTree = ""; }; @@ -1262,6 +1266,7 @@ isa = PBXGroup; children = ( 6654C7432715A4AC00901167 /* OnboardingStoreTests.swift */, + 3469F18129ACD70500A07146 /* OnboardingFlowFeatureFlagTests.swift */, ); path = OnboardingTests; sourceTree = ""; @@ -1635,7 +1640,6 @@ 9EBDF959291E654F000A1A05 /* Deeplink */, 9EBDF971291F79C9000A1A05 /* DerivationTool */, 9EBDF945291D759B000A1A05 /* DiskSpaceChecker */, - 34F682EA29A763F00022C079 /* FeatureFlagsManager */, 9EB863882922CC0E003D0F8B /* FeedbackGenerator */, 9EB863B52923C4ED003D0F8B /* FileManager */, 9EBDF981291F91B1000A1A05 /* LocalAuthentication */, @@ -1650,6 +1654,7 @@ 9EB8639E29239891003D0F8B /* URIParser */, 9E153A7129216EBD00112F41 /* UserDefaults */, 9EB863B72923C55A003D0F8B /* UserPreferencesStorage */, + 34F682EA29A763F00022C079 /* WalletConfigProvider */, 9EB86396292392F6003D0F8B /* WalletStorage */, 9E153A6A292167BF00112F41 /* ZcashSDKEnvironment */, ); @@ -1907,8 +1912,8 @@ isa = PBXGroup; children = ( 9E02B56927FED43E005B809B /* FileManagerInterface.swift */, - 9EB863B82923C6D7003D0F8B /* FileManagerLive.swift */, - 9EB863C42923C8AF003D0F8B /* FileManagerTest.swift */, + 9EB863B82923C6D7003D0F8B /* FileManagerLiveKey.swift */, + 9EB863C42923C8AF003D0F8B /* FileManagerTestKey.swift */, ); path = FileManager; sourceTree = ""; @@ -2611,10 +2616,10 @@ 0D26AEDC299E8196005260EE /* CheckCircle.swift in Sources */, 0D26AEDD299E8196005260EE /* LogStore.swift in Sources */, 0D26AEDE299E8196005260EE /* RecoveryPhraseRandomizer.swift in Sources */, - 0D26AEDF299E8196005260EE /* FileManagerTest.swift in Sources */, + 0D26AEDF299E8196005260EE /* FileManagerTestKey.swift in Sources */, 0D26AEE0299E8196005260EE /* SecItemLive.swift in Sources */, 0D26AEE1299E8196005260EE /* CircularFrameBadge.swift in Sources */, - 0D26AEE2299E8196005260EE /* FileManagerLive.swift in Sources */, + 0D26AEE2299E8196005260EE /* FileManagerLiveKey.swift in Sources */, 0D26AEE3299E8196005260EE /* AppVersionTestKey.swift in Sources */, 0D26AEE4299E8196005260EE /* CurrencySelectionView.swift in Sources */, 0D26AEE5299E8196005260EE /* RecoveryPhraseRandomizerTestKey.swift in Sources */, @@ -2835,10 +2840,10 @@ 346D41E428DF0B8600963F36 /* CheckCircle.swift in Sources */, 9E0F5745297EBA1B005304FA /* LogStore.swift in Sources */, 9EB863AA29239EB2003D0F8B /* RecoveryPhraseRandomizer.swift in Sources */, - 9EB863C52923C8AF003D0F8B /* FileManagerTest.swift in Sources */, + 9EB863C52923C8AF003D0F8B /* FileManagerTestKey.swift in Sources */, 9EB863BF2923C72C003D0F8B /* SecItemLive.swift in Sources */, 669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */, - 9EB863B92923C6D7003D0F8B /* FileManagerLive.swift in Sources */, + 9EB863B92923C6D7003D0F8B /* FileManagerLiveKey.swift in Sources */, 9EBDF97C291F7EB0000A1A05 /* AppVersionTestKey.swift in Sources */, 2E8719CD27FB0D3B0082C926 /* CurrencySelectionView.swift in Sources */, 9EB863A729239DCB003D0F8B /* RecoveryPhraseRandomizerTestKey.swift in Sources */, @@ -2989,6 +2994,7 @@ buildActionMask = 2147483647; files = ( 9E7225F12889539300DF7F17 /* SettingsSnapshotTests.swift in Sources */, + 3469F18229ACD70500A07146 /* OnboardingFlowFeatureFlagTests.swift in Sources */, 0DFE93DF272C6D4B000FCCA5 /* RecoveryPhraseBackupTests.swift in Sources */, 9EDDEAA22829610D00B4100C /* CurrencySelectionTests.swift in Sources */, 9E6713F12897F81B00A6796F /* MultiLineTextFieldTests.swift in Sources */, @@ -2997,6 +3003,7 @@ 9EDDEAA42829610D00B4100C /* TransactionAddressInputTests.swift in Sources */, 9E7CB6272874269F00A02233 /* ProfileSnapshotTests.swift in Sources */, 9E92AF0828530EBF007367AD /* View+UIImage.swift in Sources */, + 34F039B329ABCE8500CF0053 /* WalletConfigProviderTests.swift in Sources */, 34429C6E28E703CD00F2B929 /* TransactionSendingSnapshotTests.swift in Sources */, 6654C7442715A4AC00901167 /* OnboardingStoreTests.swift in Sources */, 9E94C62328AA7EE0008256E9 /* BalanceBreakdownSnapshotTests.swift in Sources */, @@ -3030,7 +3037,6 @@ 9E02B56C27FED475005B809B /* DatabaseFilesTests.swift in Sources */, 9E612C7929913F3600D09B09 /* SensitiveDataTests.swift in Sources */, 9EF8135D27ECC25E0075AF48 /* UserPreferencesStorageTests.swift in Sources */, - 34F682FC29A784660022C079 /* WalletConfigProviderTests.swift in Sources */, 9E94C62028AA7DEE008256E9 /* BalanceBreakdownTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3355,9 +3361,10 @@ ); PRODUCT_BUNDLE_IDENTIFIER = co.electriccoin.secantUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = secant; + TEST_TARGET_NAME = "secant-testnet"; }; name = Debug; }; @@ -3376,9 +3383,10 @@ ); PRODUCT_BUNDLE_IDENTIFIER = co.electriccoin.secantUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = secant; + TEST_TARGET_NAME = "secant-testnet"; }; name = Release; }; diff --git a/secant/Dependencies/FileManager/FileManagerLive.swift b/secant/Dependencies/FileManager/FileManagerLiveKey.swift similarity index 95% rename from secant/Dependencies/FileManager/FileManagerLive.swift rename to secant/Dependencies/FileManager/FileManagerLiveKey.swift index f30a7a2f..e31211d9 100644 --- a/secant/Dependencies/FileManager/FileManagerLive.swift +++ b/secant/Dependencies/FileManager/FileManagerLiveKey.swift @@ -1,5 +1,5 @@ // -// FileManagerLive.swift +// FileManagerLiveKey.swift // secant-testnet // // Created by Lukáš Korba on 15.11.2022. diff --git a/secant/Dependencies/FileManager/FileManagerTest.swift b/secant/Dependencies/FileManager/FileManagerTestKey.swift similarity index 93% rename from secant/Dependencies/FileManager/FileManagerTest.swift rename to secant/Dependencies/FileManager/FileManagerTestKey.swift index c64afd60..f84ce98e 100644 --- a/secant/Dependencies/FileManager/FileManagerTest.swift +++ b/secant/Dependencies/FileManager/FileManagerTestKey.swift @@ -1,5 +1,5 @@ // -// FileManagerTest.swift +// FileManagerTestKey.swift // secant-testnet // // Created by Lukáš Korba on 15.11.2022. diff --git a/secant/Dependencies/FeatureFlagsManager/UserDefaultsWalletConfigStorage.swift b/secant/Dependencies/WalletConfigProvider/UserDefaultsWalletConfigStorage.swift similarity index 100% rename from secant/Dependencies/FeatureFlagsManager/UserDefaultsWalletConfigStorage.swift rename to secant/Dependencies/WalletConfigProvider/UserDefaultsWalletConfigStorage.swift diff --git a/secant/Dependencies/FeatureFlagsManager/WalletConfigProvider.swift b/secant/Dependencies/WalletConfigProvider/WalletConfigProvider.swift similarity index 100% rename from secant/Dependencies/FeatureFlagsManager/WalletConfigProvider.swift rename to secant/Dependencies/WalletConfigProvider/WalletConfigProvider.swift diff --git a/secant/Dependencies/FeatureFlagsManager/WalletConfigProviderInterface.swift b/secant/Dependencies/WalletConfigProvider/WalletConfigProviderInterface.swift similarity index 100% rename from secant/Dependencies/FeatureFlagsManager/WalletConfigProviderInterface.swift rename to secant/Dependencies/WalletConfigProvider/WalletConfigProviderInterface.swift diff --git a/secant/Dependencies/FeatureFlagsManager/WalletConfigProviderLiveKey.swift b/secant/Dependencies/WalletConfigProvider/WalletConfigProviderLiveKey.swift similarity index 100% rename from secant/Dependencies/FeatureFlagsManager/WalletConfigProviderLiveKey.swift rename to secant/Dependencies/WalletConfigProvider/WalletConfigProviderLiveKey.swift diff --git a/secant/Dependencies/FeatureFlagsManager/WalletConfigProviderTestKey.swift b/secant/Dependencies/WalletConfigProvider/WalletConfigProviderTestKey.swift similarity index 89% rename from secant/Dependencies/FeatureFlagsManager/WalletConfigProviderTestKey.swift rename to secant/Dependencies/WalletConfigProvider/WalletConfigProviderTestKey.swift index 40790347..1727e836 100644 --- a/secant/Dependencies/FeatureFlagsManager/WalletConfigProviderTestKey.swift +++ b/secant/Dependencies/WalletConfigProvider/WalletConfigProviderTestKey.swift @@ -6,7 +6,6 @@ // import ComposableArchitecture -import Foundation import XCTestDynamicOverlay extension WalletConfigProviderClient: TestDependencyKey { @@ -16,7 +15,7 @@ extension WalletConfigProviderClient: TestDependencyKey { } extension WalletConfigProviderClient { - static let `default` = Self( + static let noOp = Self( load: { WalletConfig.default } ) } diff --git a/secant/Features/OnboardingFlow/OnboardingFlowStore.swift b/secant/Features/OnboardingFlow/OnboardingFlowStore.swift index c28b55b7..7ae1c1fa 100644 --- a/secant/Features/OnboardingFlow/OnboardingFlowStore.swift +++ b/secant/Features/OnboardingFlow/OnboardingFlowStore.swift @@ -27,10 +27,12 @@ struct OnboardingFlowReducer: ReducerProtocol { let badge: Badge } - var steps: IdentifiedArrayOf = Self.onboardingSteps + var destination: Destination? + var walletConfig: WalletConfig + var importWalletState: ImportWalletReducer.State var index = 0 var skippedAtindex: Int? - var destination: Destination? + var steps: IdentifiedArrayOf = Self.onboardingSteps var currentStep: Step { steps[index] } var isFinalStep: Bool { steps.count == index + 1 } @@ -42,19 +44,17 @@ struct OnboardingFlowReducer: ReducerProtocol { guard index != 0 else { return .zero } return stepOffset * CGFloat(index) } - - /// Import Wallet - var importWalletState: ImportWalletReducer.State } enum Action: Equatable { - case next case back - case skip - case updateDestination(OnboardingFlowReducer.State.Destination?) case createNewWallet case importExistingWallet case importWallet(ImportWalletReducer.Action) + case next + case onAppear + case skip + case updateDestination(OnboardingFlowReducer.State.Destination?) } var body: some ReducerProtocol { @@ -64,6 +64,12 @@ struct OnboardingFlowReducer: ReducerProtocol { Reduce { state, action in switch action { + case .onAppear: + if !state.walletConfig.isEnabled(.onboardingFlow) { + return EffectTask(value: .skip) + } + return .none + case .back: guard state.index > 0 else { return .none } if let skippedFrom = state.skippedAtindex { diff --git a/secant/Features/OnboardingFlow/OnboardingFlowView.swift b/secant/Features/OnboardingFlow/OnboardingFlowView.swift index 1be0be2d..3d014ea7 100644 --- a/secant/Features/OnboardingFlow/OnboardingFlowView.swift +++ b/secant/Features/OnboardingFlow/OnboardingFlowView.swift @@ -9,38 +9,42 @@ import SwiftUI import ComposableArchitecture struct OnboardingScreen: View { - let store: Store + let store: OnboardingFlowStore var body: some View { - VStack { - ZStack { - OnboardingHeaderView( - store: store.scope( - state: { state in - OnboardingHeaderView.ViewState( - isInitialStep: state.isInitialStep, - isFinalStep: state.isFinalStep - ) - }, - action: { action in - switch action { - case .back: return .back - case .skip: return .skip + WithViewStore(store) { viewStore in + VStack { + ZStack { + OnboardingHeaderView( + store: store.scope( + state: { state in + OnboardingHeaderView.ViewState( + walletConfig: state.walletConfig, + isInitialStep: state.isInitialStep, + isFinalStep: state.isFinalStep + ) + }, + action: { action in + switch action { + case .back: return .back + case .skip: return .skip + } } - } + ) ) - ) - .zIndex(1) + .zIndex(1) + + OnboardingContentView(store: store) + } - OnboardingContentView(store: store) + Spacer() + + OnboardingFooterView(store: store) } - - Spacer() - - OnboardingFooterView(store: store) + .navigationBarHidden(true) + .applyScreenBackground() + .onAppear { viewStore.send(.onAppear) } } - .navigationBarHidden(true) - .applyScreenBackground() } } @@ -51,6 +55,7 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( initialState: OnboardingFlowReducer.State( + walletConfig: .default, importWalletState: .placeholder ), reducer: OnboardingFlowReducer() @@ -62,6 +67,7 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( initialState: OnboardingFlowReducer.State( + walletConfig: .default, importWalletState: .placeholder ), reducer: OnboardingFlowReducer() @@ -73,6 +79,7 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( initialState: OnboardingFlowReducer.State( + walletConfig: .default, importWalletState: .placeholder ), reducer: OnboardingFlowReducer() @@ -85,6 +92,7 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( initialState: OnboardingFlowReducer.State( + walletConfig: .default, importWalletState: .placeholder ), reducer: OnboardingFlowReducer() @@ -96,6 +104,7 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( initialState: OnboardingFlowReducer.State( + walletConfig: .default, importWalletState: .placeholder ), reducer: OnboardingFlowReducer() @@ -107,6 +116,7 @@ struct OnboardingScreen_Previews: PreviewProvider { OnboardingScreen( store: Store( initialState: OnboardingFlowReducer.State( + walletConfig: .default, importWalletState: .placeholder ), reducer: OnboardingFlowReducer() diff --git a/secant/Features/OnboardingFlow/Views/OnboardingContentView.swift b/secant/Features/OnboardingFlow/Views/OnboardingContentView.swift index 726c7551..ee43692f 100644 --- a/secant/Features/OnboardingFlow/Views/OnboardingContentView.swift +++ b/secant/Features/OnboardingFlow/Views/OnboardingContentView.swift @@ -59,8 +59,9 @@ struct OnboardingContentView_Previews: PreviewProvider { static var previews: some View { let store = Store( initialState: OnboardingFlowReducer.State( - index: 0, - importWalletState: .placeholder + walletConfig: .default, + importWalletState: .placeholder, + index: 0 ), reducer: OnboardingFlowReducer() ) @@ -85,6 +86,7 @@ extension OnboardingContentView_Previews { store: store.scope( state: { state in OnboardingHeaderView.ViewState( + walletConfig: state.walletConfig, isInitialStep: state.isInitialStep, isFinalStep: state.isFinalStep ) diff --git a/secant/Features/OnboardingFlow/Views/OnboardingFooterView.swift b/secant/Features/OnboardingFlow/Views/OnboardingFooterView.swift index dca4d322..1eda679c 100644 --- a/secant/Features/OnboardingFlow/Views/OnboardingFooterView.swift +++ b/secant/Features/OnboardingFlow/Views/OnboardingFooterView.swift @@ -85,8 +85,9 @@ struct OnboardingFooterView_Previews: PreviewProvider { static var previews: some View { let store = Store( initialState: OnboardingFlowReducer.State( - index: 3, - importWalletState: .placeholder + walletConfig: .default, + importWalletState: .placeholder, + index: 3 ), reducer: OnboardingFlowReducer() ) diff --git a/secant/Features/OnboardingFlow/Views/OnboardingHeaderView.swift b/secant/Features/OnboardingFlow/Views/OnboardingHeaderView.swift index be5a6f5e..19cd4210 100644 --- a/secant/Features/OnboardingFlow/Views/OnboardingHeaderView.swift +++ b/secant/Features/OnboardingFlow/Views/OnboardingHeaderView.swift @@ -10,6 +10,7 @@ import ComposableArchitecture struct OnboardingHeaderView: View { struct ViewState: Equatable { + let walletConfig: WalletConfig let isInitialStep: Bool let isFinalStep: Bool } @@ -26,7 +27,7 @@ struct OnboardingHeaderView: View { WithViewStore(self.store) { viewStore in VStack { HStack { - if !viewStore.isInitialStep { + if !viewStore.isInitialStep && viewStore.walletConfig.isEnabled(.onboardingFlow) { Button("Back") { viewStore.send(.back, animation: .easeInOut(duration: animationDuration)) } @@ -63,8 +64,9 @@ struct OnboardingHeaderView_Previews: PreviewProvider { static var previews: some View { let store = Store( initialState: OnboardingFlowReducer.State( - index: 0, - importWalletState: .placeholder + walletConfig: .default, + importWalletState: .placeholder, + index: 0 ), reducer: OnboardingFlowReducer() ) @@ -73,6 +75,7 @@ struct OnboardingHeaderView_Previews: PreviewProvider { store: store.scope( state: { state in OnboardingHeaderView.ViewState( + walletConfig: state.walletConfig, isInitialStep: state.isInitialStep, isFinalStep: state.isFinalStep ) diff --git a/secant/Features/Root/RootDestination.swift b/secant/Features/Root/RootDestination.swift index d854b8c8..326fd503 100644 --- a/secant/Features/Root/RootDestination.swift +++ b/secant/Features/Root/RootDestination.swift @@ -14,13 +14,13 @@ import ZcashLightClientKit extension RootReducer { struct DestinationState: Equatable { enum Destination: Equatable { - case welcome - case startup - case onboarding - case sandbox case home - case phraseValidation + case onboarding case phraseDisplay + case phraseValidation + case sandbox + case startup + case welcome } var internalDestination: Destination = .welcome diff --git a/secant/Features/Root/RootInitialization.swift b/secant/Features/Root/RootInitialization.swift index fee81e6d..7ee3a4c4 100644 --- a/secant/Features/Root/RootInitialization.swift +++ b/secant/Features/Root/RootInitialization.swift @@ -6,6 +6,7 @@ // import ComposableArchitecture +import Foundation /// In this file is a collection of helpers that control all state and action related operations /// for the `RootReducer` with a connection to the app/wallet initialization and erasure of the wallet. @@ -16,16 +17,39 @@ extension RootReducer { case checkWalletInitialization case configureCrashReporter case createNewWallet + case checkWalletConfig case initializeSDK + case initialSetups case nukeWallet case respondToWalletInitializationState(InitializationState) + case walletConfigChanged(WalletConfig) } - // swiftlint:disable:next cyclomatic_complexity + // swiftlint:disable:next cyclomatic_complexity function_body_length func initializationReduce() -> Reduce { Reduce { state, action in switch action { case .initialization(.appDelegate(.didFinishLaunching)): + return EffectTask(value: .initialization(.checkWalletConfig)) + .delay(for: 0.02, scheduler: mainQueue) + .eraseToEffect() + + case .initialization(.checkWalletConfig): + return .run { send in + let walletConfig = await walletConfigProvider.load() + if walletConfig == WalletConfig.default { + await send(.initialization(.initialSetups)) + } else { + await send(.initialization(.walletConfigChanged(walletConfig))) + } + } + + case .initialization(.walletConfigChanged(let walletConfig)): + state.walletConfig = walletConfig + state.onboardingState.walletConfig = walletConfig + return 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 LoggerProxy.event(".appDelegate(.didFinishLaunching)") /// We need to fetch data from keychain, in order to be 100% sure the keychain can be read we delay the check a bit diff --git a/secant/Features/Root/RootStore.swift b/secant/Features/Root/RootStore.swift index 257c6a4c..8a7570b7 100644 --- a/secant/Features/Root/RootStore.swift +++ b/secant/Features/Root/RootStore.swift @@ -10,6 +10,7 @@ struct RootReducer: ReducerProtocol { struct State: Equatable { var appInitializationState: InitializationState = .uninitialized var destinationState: DestinationState + var walletConfig: WalletConfig var homeState: HomeReducer.State var onboardingState: OnboardingFlowReducer.State var phraseValidationState: RecoveryPhraseValidationFlowReducer.State @@ -34,6 +35,7 @@ struct RootReducer: ReducerProtocol { @Dependency(\.databaseFiles) var databaseFiles @Dependency(\.deeplink) var deeplink @Dependency(\.derivationTool) var derivationTool + @Dependency(\.walletConfigProvider) var walletConfigProvider @Dependency(\.mainQueue) var mainQueue @Dependency(\.mnemonic) var mnemonic @Dependency(\.randomRecoveryPhrase) var randomRecoveryPhrase @@ -159,8 +161,10 @@ extension RootReducer.State { static var placeholder: Self { .init( destinationState: .placeholder, + walletConfig: .default, homeState: .placeholder, onboardingState: .init( + walletConfig: .default, importWalletState: .placeholder ), phraseValidationState: .placeholder, diff --git a/secant/Models/WalletConfig.swift b/secant/Models/WalletConfig.swift index bda3fceb..7856ab25 100644 --- a/secant/Models/WalletConfig.swift +++ b/secant/Models/WalletConfig.swift @@ -9,11 +9,12 @@ enum FeatureFlag: String, CaseIterable, Codable { // These two flags should stay here because those are used in tests. It's not super nice but there is probably no other way. case testFlag1 case testFlag2 + case onboardingFlow var enabledByDefault: Bool { switch self { - case .testFlag1, .testFlag2: - return false + case .testFlag1, .testFlag2: return false + case .onboardingFlow: return false } } } diff --git a/secantTests/DeeplinkTests/DeeplinkTests.swift b/secantTests/DeeplinkTests/DeeplinkTests.swift index a94b92a5..0ed73ebd 100644 --- a/secantTests/DeeplinkTests/DeeplinkTests.swift +++ b/secantTests/DeeplinkTests/DeeplinkTests.swift @@ -82,20 +82,21 @@ class DeeplinkTests: XCTestCase { let store = TestStore( initialState: appState, reducer: RootReducer() - ) { dependencies in - dependencies.deeplink = DeeplinkClient( - resolveDeeplinkURL: { _, _ in Deeplink.Destination.home } - ) - let synchronizer = NoopSDKSynchronizer() - synchronizer.updateStateChanged(.synced) - dependencies.sdkSynchronizer = synchronizer - } + ) + + store.dependencies.deeplink = DeeplinkClient( + resolveDeeplinkURL: { _, _ in Deeplink.Destination.home } + ) + let synchronizer = NoopSDKSynchronizer() + synchronizer.updateStateChanged(.synced) + store.dependencies.sdkSynchronizer = synchronizer + store.dependencies.walletConfigProvider = .noOp guard let url = URL(string: "zcash:///home") else { return XCTFail("Deeplink: 'testDeeplinkRequest_homeURL' URL is expected to be valid.") } - _ = await store.send(.destination(.deeplink(url))) + await store.send(.destination(.deeplink(url))) await store.receive(.destination(.deeplinkHome)) { state in state.destinationState.destination = .home @@ -136,7 +137,7 @@ class DeeplinkTests: XCTestCase { return XCTFail("Deeplink: 'testDeeplinkRequest_sendURL_amount' URL is expected to be valid.") } - _ = await store.send(.destination(.deeplink(url))) + await store.send(.destination(.deeplink(url))) let amount = Zatoshi(123_000_000) let address = "address" diff --git a/secantTests/HomeTests/HomeTests.swift b/secantTests/HomeTests/HomeTests.swift index f2fb0fbf..48641e07 100644 --- a/secantTests/HomeTests/HomeTests.swift +++ b/secantTests/HomeTests/HomeTests.swift @@ -106,7 +106,7 @@ class HomeTests: XCTestCase { reducer: HomeReducer() ) - _ = await store.send(.profile(.settings(.quickRescan))) { state in + await store.send(.profile(.settings(.quickRescan))) { state in state.destination = nil } @@ -131,7 +131,7 @@ class HomeTests: XCTestCase { reducer: HomeReducer() ) - _ = await store.send(.profile(.settings(.fullRescan))) { state in + await store.send(.profile(.settings(.fullRescan))) { state in state.destination = nil } diff --git a/secantTests/OnboardingTests/OnboardingFlowFeatureFlagTests.swift b/secantTests/OnboardingTests/OnboardingFlowFeatureFlagTests.swift new file mode 100644 index 00000000..db7db4d7 --- /dev/null +++ b/secantTests/OnboardingTests/OnboardingFlowFeatureFlagTests.swift @@ -0,0 +1,64 @@ +// +// OnboardingFlowFeatureFlagTests.swift +// secantTests +// +// Created by Lukáš Korba on 23.02.2023. +// + +import XCTest +@testable import secant_testnet +import ComposableArchitecture + +class OnboardingFlowFeatureFlagTests: XCTestCase { + override func setUp() { + super.setUp() + + UserDefaultsWalletConfigStorage().clearAll() + } + + func testOnboardingFlowOffByDefault() throws { + XCTAssertFalse(WalletConfig.default.isEnabled(.onboardingFlow)) + } + + func testOnboardingFlowOff_SkipEducation() { + let initialState = OnboardingFlowReducer.State( + walletConfig: .default, + importWalletState: .placeholder + ) + + let store = TestStore( + initialState: initialState, + reducer: OnboardingFlowReducer() + ) + + store.send(.onAppear) + + store.receive(.skip) { state in + state.index = initialState.steps.count - 1 + state.skippedAtindex = 0 + } + } + + func testOnboardingFlowOn_StartEducation() { + var defaultRawFlags = WalletConfig.default.flags + defaultRawFlags[.onboardingFlow] = false + let flags = WalletConfig(flags: defaultRawFlags) + + let initialState = OnboardingFlowReducer.State( + walletConfig: flags, + importWalletState: .placeholder + ) + + let store = TestStore( + initialState: initialState, + reducer: OnboardingFlowReducer() + ) + + store.send(.onAppear) + + store.receive(.skip) { state in + state.index = initialState.steps.count - 1 + state.skippedAtindex = 0 + } + } +} diff --git a/secantTests/OnboardingTests/OnboardingStoreTests.swift b/secantTests/OnboardingTests/OnboardingStoreTests.swift index 65c69783..1b3f6923 100644 --- a/secantTests/OnboardingTests/OnboardingStoreTests.swift +++ b/secantTests/OnboardingTests/OnboardingStoreTests.swift @@ -13,6 +13,7 @@ class OnboardingStoreTests: XCTestCase { func testIncrementingOnboarding() { let store = TestStore( initialState: OnboardingFlowReducer.State( + walletConfig: .default, importWalletState: .placeholder ), reducer: OnboardingFlowReducer() @@ -52,8 +53,9 @@ class OnboardingStoreTests: XCTestCase { func testIncrementingPastTotalStepsDoesNothing() { let store = TestStore( initialState: OnboardingFlowReducer.State( - index: 3, - importWalletState: .placeholder + walletConfig: .default, + importWalletState: .placeholder, + index: 3 ), reducer: OnboardingFlowReducer() ) @@ -65,8 +67,9 @@ class OnboardingStoreTests: XCTestCase { func testDecrementingOnboarding() { let store = TestStore( initialState: OnboardingFlowReducer.State( - index: 2, - importWalletState: .placeholder + walletConfig: .default, + importWalletState: .placeholder, + index: 2 ), reducer: OnboardingFlowReducer() ) @@ -95,6 +98,7 @@ class OnboardingStoreTests: XCTestCase { func testDecrementingPastFirstStepDoesNothing() { let store = TestStore( initialState: OnboardingFlowReducer.State( + walletConfig: .default, importWalletState: .placeholder ), reducer: OnboardingFlowReducer() @@ -109,8 +113,9 @@ class OnboardingStoreTests: XCTestCase { let store = TestStore( initialState: OnboardingFlowReducer.State( - index: initialIndex, - importWalletState: .placeholder + walletConfig: .default, + importWalletState: .placeholder, + index: initialIndex ), reducer: OnboardingFlowReducer() ) diff --git a/secantTests/ProfileTests/ProfileTests.swift b/secantTests/ProfileTests/ProfileTests.swift index f54f9725..05b4066e 100644 --- a/secantTests/ProfileTests/ProfileTests.swift +++ b/secantTests/ProfileTests/ProfileTests.swift @@ -26,7 +26,7 @@ class ProfileTests: XCTestCase { network: .testnet ) - _ = await store.send(.onAppear) { state in + await store.send(.onAppear) { state in state.addressDetailsState.uAddress = uAddress state.appVersion = "0.0.1" state.appBuild = "31" diff --git a/secantTests/RootTests/AppInitializationTests.swift b/secantTests/RootTests/AppInitializationTests.swift index aa7454b8..fdcdde39 100644 --- a/secantTests/RootTests/AppInitializationTests.swift +++ b/secantTests/RootTests/AppInitializationTests.swift @@ -11,15 +11,8 @@ import ComposableArchitecture class AppInitializationTests: XCTestCase { /// This integration test starts with finishing the app launch and triggering bunch of initialization procedures. - /// 1. The app calls .checkWalletInitialization delayed by 0.02 seconds to ensure keychain is successfully operational. - /// 2. The .respondToWalletInitializationState is triggered to decide the state of the wallet. - /// 3. The .initializeSDK is triggered to set the state of the app and preparing the synchronizer. - /// 4. The .checkBackupPhraseValidation is triggered to check the validation state. - /// 5. The user hasn't finished the backup phrase test so the display phrase is presented. @MainActor func testDidFinishLaunching_to_InitializedWallet() async throws { // setup the store and environment to be fully mocked - let testScheduler = DispatchQueue.test - let recoveryPhrase = RecoveryPhrase(words: try MnemonicClient.mock.randomMnemonicWords().map { $0.redacted }) let phraseValidationState = RecoveryPhraseValidationFlowReducer.State( @@ -72,8 +65,10 @@ class AppInitializationTests: XCTestCase { let appState = RootReducer.State( destinationState: .placeholder, + walletConfig: .default, homeState: .placeholder, onboardingState: .init( + walletConfig: .default, importWalletState: .placeholder ), phraseValidationState: phraseValidationState, @@ -87,123 +82,108 @@ class AppInitializationTests: XCTestCase { let store = TestStore( initialState: appState, reducer: RootReducer() - ) { dependencies in - dependencies.databaseFiles = .noOp - dependencies.databaseFiles.areDbFilesPresentFor = { _ in true } - dependencies.derivationTool = .liveValue - dependencies.mainQueue = testScheduler.eraseToAnyScheduler() - dependencies.mnemonic = .mock - dependencies.randomRecoveryPhrase = recoveryPhraseRandomizer - dependencies.walletStorage.exportWallet = { .placeholder } - dependencies.walletStorage.areKeysPresent = { true } - } + ) + + store.dependencies.databaseFiles = .noOp + store.dependencies.databaseFiles.areDbFilesPresentFor = { _ in true } + store.dependencies.derivationTool = .liveValue + store.dependencies.mainQueue = .immediate + store.dependencies.mnemonic = .mock + store.dependencies.randomRecoveryPhrase = recoveryPhraseRandomizer + store.dependencies.walletStorage.exportWallet = { .placeholder } + store.dependencies.walletStorage.areKeysPresent = { true } + store.dependencies.walletConfigProvider = .noOp // Root of the test, the app finished the launch process and triggers the checks and initializations. - _ = await store.send(.initialization(.appDelegate(.didFinishLaunching))) + await store.send(.initialization(.appDelegate(.didFinishLaunching))) - // the 0.02 delay ensures keychain is ready - await testScheduler.advance(by: 0.02) + await store.receive(.initialization(.checkWalletConfig)) + + await store.receive(.initialization(.initialSetups)) - // ad 1. await store.receive(.initialization(.configureCrashReporter)) - // ad 2. await store.receive(.initialization(.checkWalletInitialization)) - // ad 3. await store.receive(.initialization(.respondToWalletInitializationState(.initialized))) - // ad 4. await store.receive(.initialization(.initializeSDK)) { state in state.storedWallet = .placeholder } - // ad 5. + await store.receive(.initialization(.checkBackupPhraseValidation)) { state in state.appInitializationState = .initialized } - // the 3.0 delay ensures the welcome screen is visible till the initialization is done - await testScheduler.advance(by: 3.00) - - // ad 5. await store.receive(.destination(.updateDestination(.phraseDisplay))) { state in state.destinationState.previousDestination = .welcome state.destinationState.internalDestination = .phraseDisplay } + + await store.finish() } /// Integration test validating the side effects work together properly when no wallet is stored but database files are present. - /// 1. The app calls .checkWalletInitialization delayed by 0.02 seconds to ensure keychain is successfully operational. - /// 2. The .respondToWalletInitializationState is triggered to decide the state of the wallet. - func testDidFinishLaunching_to_KeysMissing() throws { - // setup the store and environment to be fully mocked - let testScheduler = DispatchQueue.test - + @MainActor func testDidFinishLaunching_to_KeysMissing() async throws { let store = TestStore( initialState: .placeholder, reducer: RootReducer() - ) { dependencies in - dependencies.databaseFiles = .noOp - dependencies.databaseFiles.areDbFilesPresentFor = { _ in true } - dependencies.mainQueue = testScheduler.eraseToAnyScheduler() - dependencies.walletStorage = .noOp - } + ) + + store.dependencies.databaseFiles = .noOp + store.dependencies.databaseFiles.areDbFilesPresentFor = { _ in true } + store.dependencies.walletStorage = .noOp + store.dependencies.mainQueue = .immediate + store.dependencies.walletConfigProvider = .noOp // Root of the test, the app finished the launch process and triggers the checks and initializations. - store.send(.initialization(.appDelegate(.didFinishLaunching))) - - // the 0.02 delay ensures keychain is ready - testScheduler.advance(by: 0.02) - - // ad 1. - store.receive(.initialization(.configureCrashReporter)) + await store.send(.initialization(.appDelegate(.didFinishLaunching))) - // ad 2 - store.receive(.initialization(.checkWalletInitialization)) + await store.receive(.initialization(.checkWalletConfig)) + + await store.receive(.initialization(.initialSetups)) - // ad 3. - store.receive(.initialization(.respondToWalletInitializationState(.keysMissing))) { state in + await store.receive(.initialization(.configureCrashReporter)) + + await store.receive(.initialization(.checkWalletInitialization)) + + await store.receive(.initialization(.respondToWalletInitializationState(.keysMissing))) { state in state.appInitializationState = .keysMissing } + + await store.finish() } /// Integration test validating the side effects work together properly when no wallet is stored and no database files are present. - /// 1. The app calls .checkWalletInitialization delayed by 0.02 seconds to ensure keychain is successfully operational. - /// 2. The .respondToWalletInitializationState is triggered to decide the state of the wallet. - /// 3. The wallet is not present, onboarding flow is triggered. - func testDidFinishLaunching_to_Uninitialized() throws { - // setup the store and environment to be fully mocked - let testScheduler = DispatchQueue.test - + @MainActor func testDidFinishLaunching_to_Uninitialized() async throws { let store = TestStore( initialState: .placeholder, reducer: RootReducer() - ) { dependencies in - dependencies.databaseFiles = .noOp - dependencies.mainQueue = testScheduler.eraseToAnyScheduler() - dependencies.walletStorage = .noOp - } + ) + + store.dependencies.databaseFiles = .noOp + store.dependencies.mainQueue = .immediate + store.dependencies.walletStorage = .noOp + store.dependencies.walletConfigProvider = .noOp // Root of the test, the app finished the launch process and triggers the checks and initializations. - store.send(.initialization(.appDelegate(.didFinishLaunching))) + await store.send(.initialization(.appDelegate(.didFinishLaunching))) + + await store.receive(.initialization(.checkWalletConfig)) + + await store.receive(.initialization(.initialSetups)) + + await store.receive(.initialization(.configureCrashReporter)) + + await store.receive(.initialization(.checkWalletInitialization)) + + await store.receive(.initialization(.respondToWalletInitializationState(.uninitialized))) - // the 0.02 delay ensures keychain is ready - // the 3.0 delay ensures the welcome screen is visible till the initialization check is done - testScheduler.advance(by: 3.02) - - // ad 1. - store.receive(.initialization(.configureCrashReporter)) - - // ad 2. - store.receive(.initialization(.checkWalletInitialization)) - - // ad 3. - store.receive(.initialization(.respondToWalletInitializationState(.uninitialized))) - - // ad 4. - store.receive(.destination(.updateDestination(.onboarding))) { state in + await store.receive(.destination(.updateDestination(.onboarding))) { state in state.destinationState.previousDestination = .welcome state.destinationState.internalDestination = .onboarding } + + await store.finish() } } diff --git a/secantTests/SettingsTests/SettingsTests.swift b/secantTests/SettingsTests/SettingsTests.swift index a328de46..933eb066 100644 --- a/secantTests/SettingsTests/SettingsTests.swift +++ b/secantTests/SettingsTests/SettingsTests.swift @@ -57,7 +57,7 @@ class SettingsTests: XCTestCase { dependencies.walletStorage = mockedWalletStorage } - _ = await store.send(.backupWalletAccessRequest) + await store.send(.backupWalletAccessRequest) await store.receive(.backupWallet) { state in state.phraseDisplayState.phrase = RecoveryPhrase(words: mnemonic.components(separatedBy: " ").map { $0.redacted }) @@ -75,7 +75,7 @@ class SettingsTests: XCTestCase { $0.localAuthentication = .mockAuthenticationFailed } - _ = await store.send(.backupWalletAccessRequest) + await store.send(.backupWalletAccessRequest) await store.finish() } @@ -86,7 +86,7 @@ class SettingsTests: XCTestCase { reducer: SettingsReducer() ) - _ = await store.send(.rescanBlockchain) { state in + await store.send(.rescanBlockchain) { state in state.rescanDialog = .init( title: TextState("Rescan"), message: TextState("Select the rescan you want"), @@ -118,7 +118,7 @@ class SettingsTests: XCTestCase { reducer: SettingsReducer() ) - _ = await store.send(.cancelRescan) { state in + await store.send(.cancelRescan) { state in state.rescanDialog = nil } } @@ -142,7 +142,7 @@ class SettingsTests: XCTestCase { reducer: SettingsReducer() ) - _ = await store.send(.quickRescan) { state in + await store.send(.quickRescan) { state in state.rescanDialog = nil } } @@ -166,7 +166,7 @@ class SettingsTests: XCTestCase { reducer: SettingsReducer() ) - _ = await store.send(.fullRescan) { state in + await store.send(.fullRescan) { state in state.rescanDialog = nil } } @@ -192,7 +192,7 @@ class SettingsTests: XCTestCase { store.dependencies.logsHandler = LogsHandlerClient(exportAndStoreLogs: { _, _, _ in }) - _ = await store.send(.exportLogs) { state in + await store.send(.exportLogs) { state in state.exportLogsDisabled = true } @@ -222,7 +222,7 @@ class SettingsTests: XCTestCase { reducer: SettingsReducer() ) - _ = await store.send(.logsShareFinished) { state in + await store.send(.logsShareFinished) { state in state.isSharingLogs = false } } diff --git a/secantTests/SnapshotTests/AddressDetailsSnapshotTests/AddressDetailsSnapshotTests.swift b/secantTests/SnapshotTests/AddressDetailsSnapshotTests/AddressDetailsSnapshotTests.swift index e1ddf232..275419e0 100644 --- a/secantTests/SnapshotTests/AddressDetailsSnapshotTests/AddressDetailsSnapshotTests.swift +++ b/secantTests/SnapshotTests/AddressDetailsSnapshotTests/AddressDetailsSnapshotTests.swift @@ -22,6 +22,7 @@ class AddressDetailsSnapshotTests: XCTestCase { let store = Store( initialState: AddressDetailsReducer.State(uAddress: uAddress), reducer: AddressDetailsReducer() + .dependency(\.walletConfigProvider, .noOp) ) addAttachments(AddressDetailsView(store: store)) diff --git a/secantTests/SnapshotTests/OnboardingSnapshotTests/OnboardingSnapshotTests.swift b/secantTests/SnapshotTests/OnboardingSnapshotTests/OnboardingSnapshotTests.swift index 844424f8..c5f99e21 100644 --- a/secantTests/SnapshotTests/OnboardingSnapshotTests/OnboardingSnapshotTests.swift +++ b/secantTests/SnapshotTests/OnboardingSnapshotTests/OnboardingSnapshotTests.swift @@ -12,7 +12,10 @@ import ComposableArchitecture class OnboardingSnapshotTests: XCTestCase { func testOnboardingFlowSnapshot() throws { let store = OnboardingFlowStore( - initialState: OnboardingFlowReducer.State(importWalletState: .placeholder), + initialState: OnboardingFlowReducer.State( + walletConfig: .default, + importWalletState: .placeholder + ), reducer: OnboardingFlowReducer() ) let viewStore = ViewStore(store) diff --git a/secantTests/WalletConfigProvider/WalletConfigProviderTests.swift b/secantTests/WalletConfigProviderTests/WalletConfigProviderTests.swift similarity index 100% rename from secantTests/WalletConfigProvider/WalletConfigProviderTests.swift rename to secantTests/WalletConfigProviderTests/WalletConfigProviderTests.swift