diff --git a/README.md b/README.md index 287257b..7c95d7a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Clean, Simple and Composable Routing for iOS Apps -This is the [second part](https://cassiuspacheco.com/applying-dependency-injection-to-composable-routing-for-ios-apps-ck7v10xaz01h9zis1oksoe7h0) of a series of blog posts about [Clean, Simple and Composable Routing for iOS Apps](https://hashnode.com/series/clean-simple-and-composable-routing-for-ios-apps-ck7vm42k401n4zis1wu4ar2od). +This is the [third part](https://cassiuspacheco.com/unit-testing-composable-routing-in-swift-for-ios-apps-part-3-ck85u05av0018shs1vdc64t23) of a series of blog posts about [Clean, Simple and Composable Routing for iOS Apps](https://hashnode.com/series/clean-simple-and-composable-routing-for-ios-apps-ck7vm42k401n4zis1wu4ar2od). ## App's Flow Diagram diff --git a/RoutingExample.xcodeproj/project.pbxproj b/RoutingExample.xcodeproj/project.pbxproj index 0b2ea45..601bda1 100644 --- a/RoutingExample.xcodeproj/project.pbxproj +++ b/RoutingExample.xcodeproj/project.pbxproj @@ -11,12 +11,28 @@ 6A46A770241905EB00ED7426 /* WishlistTabRoute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A46A76F241905EB00ED7426 /* WishlistTabRoute.swift */; }; 6A46A773241908F400ED7426 /* DependencyGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A46A772241908F400ED7426 /* DependencyGraph.swift */; }; 6A51EA05241DB801004FA2D0 /* DependencyContainer in Frameworks */ = {isa = PBXBuildFile; productRef = 6A51EA04241DB801004FA2D0 /* DependencyContainer */; }; + 6A5E44F6241C9A7A001CA6C0 /* TransitionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E44F5241C9A7A001CA6C0 /* TransitionMock.swift */; }; + 6A5E44FE241C9CB0001CA6C0 /* DependencyContainerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E44FD241C9CB0001CA6C0 /* DependencyContainerMock.swift */; }; + 6A5E4501241C9D3B001CA6C0 /* LoginViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E4500241C9D3B001CA6C0 /* LoginViewModelMock.swift */; }; + 6A5E4503241C9DA1001CA6C0 /* ForgottenPasswordViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E4502241C9DA1001CA6C0 /* ForgottenPasswordViewModelMock.swift */; }; + 6A5E4505241C9E1F001CA6C0 /* SignUpViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E4504241C9E1F001CA6C0 /* SignUpViewModelMock.swift */; }; + 6A5E4507241C9E70001CA6C0 /* PopUpViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E4506241C9E70001CA6C0 /* PopUpViewModelMock.swift */; }; + 6A5E4509241C9EBC001CA6C0 /* ProductViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E4508241C9EBC001CA6C0 /* ProductViewModelMock.swift */; }; + 6A5E450C241C9F81001CA6C0 /* AddVoiceShortcutButtonDelegateMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E450B241C9F81001CA6C0 /* AddVoiceShortcutButtonDelegateMock.swift */; }; + 6A5E450E241C9FB7001CA6C0 /* ShopViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E450D241C9FB7001CA6C0 /* ShopViewModelMock.swift */; }; + 6A5E4510241C9FE7001CA6C0 /* WishlistViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E450F241C9FE6001CA6C0 /* WishlistViewModelMock.swift */; }; + 6A5E4513241CA1E6001CA6C0 /* LoginRouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E4512241CA1E6001CA6C0 /* LoginRouteTests.swift */; }; + 6A5E4515241CA48E001CA6C0 /* RouterMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E4514241CA48E001CA6C0 /* RouterMock.swift */; }; + 6A5E4518241CAD67001CA6C0 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E4517241CAD67001CA6C0 /* UIViewController.swift */; }; + 6A5E451A241CAEC7001CA6C0 /* PopUpRouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E4519241CAEC7001CA6C0 /* PopUpRouteTests.swift */; }; + 6A5E451C241CAF97001CA6C0 /* ShopTabRouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E451B241CAF97001CA6C0 /* ShopTabRouteTests.swift */; }; + 6A5E451E241CB002001CA6C0 /* WishlistTabRouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E451D241CB002001CA6C0 /* WishlistTabRouteTests.swift */; }; + 6A5E4521241CB055001CA6C0 /* DefaultRouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5E4520241CB055001CA6C0 /* DefaultRouterTests.swift */; }; 6A61168224136AE30099C25F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A61168124136AE30099C25F /* AppDelegate.swift */; }; 6A61168424136AE30099C25F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A61168324136AE30099C25F /* SceneDelegate.swift */; }; 6A61168624136AE30099C25F /* ShopViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A61168524136AE30099C25F /* ShopViewController.swift */; }; 6A61168D24136AE50099C25F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A61168C24136AE50099C25F /* Assets.xcassets */; }; 6A61169024136AE50099C25F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6A61168E24136AE50099C25F /* LaunchScreen.storyboard */; }; - 6A61169B24136AE50099C25F /* RoutingExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A61169A24136AE50099C25F /* RoutingExampleTests.swift */; }; 6A6116A624136C5C0099C25F /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A6116A524136C5C0099C25F /* MainTabBarController.swift */; }; 6A6116A824136EF30099C25F /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A6116A724136EF30099C25F /* LoginViewController.swift */; }; 6A6116AA24136EFE0099C25F /* ProductViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A6116A924136EFE0099C25F /* ProductViewController.swift */; }; @@ -26,6 +42,19 @@ 6A6116B924136FFD0099C25F /* WishlistViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A6116B824136FFD0099C25F /* WishlistViewController.swift */; }; 6A6116BD241372270099C25F /* LayoutHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A6116BC241372270099C25F /* LayoutHelper.swift */; }; 6A6116BF241372C70099C25F /* DefaultButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A6116BE241372C70099C25F /* DefaultButton.swift */; }; + 6A79A72A241CB55B00CCED5C /* ViewControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A729241CB55B00CCED5C /* ViewControllerMock.swift */; }; + 6A79A72C241CB67300CCED5C /* DeeplinkRouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A72B241CB67300CCED5C /* DeeplinkRouterTests.swift */; }; + 6A79A72E241CBC5900CCED5C /* ProductRouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A72D241CBC5900CCED5C /* ProductRouteTests.swift */; }; + 6A79A730241CBCB900CCED5C /* ForgottenPasswordRouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A72F241CBCB900CCED5C /* ForgottenPasswordRouteTests.swift */; }; + 6A79A732241CBD2F00CCED5C /* SignUpRouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A731241CBD2F00CCED5C /* SignUpRouteTests.swift */; }; + 6A79A735241CBDAB00CCED5C /* ShopViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A734241CBDAB00CCED5C /* ShopViewModelTests.swift */; }; + 6A79A737241CBFD100CCED5C /* RoutesMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A736241CBFD100CCED5C /* RoutesMock.swift */; }; + 6A79A739241CC17C00CCED5C /* WishlistViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A738241CC17C00CCED5C /* WishlistViewModelTests.swift */; }; + 6A79A73B241CC1A600CCED5C /* ProductViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A73A241CC1A600CCED5C /* ProductViewModelTests.swift */; }; + 6A79A73D241CC23D00CCED5C /* PopUpViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A73C241CC23D00CCED5C /* PopUpViewModelTests.swift */; }; + 6A79A73F241CC2EC00CCED5C /* SignUpViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A73E241CC2EC00CCED5C /* SignUpViewModelTests.swift */; }; + 6A79A741241CC36F00CCED5C /* LoginViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A740241CC36F00CCED5C /* LoginViewModelTests.swift */; }; + 6A79A743241CC3B200CCED5C /* ForgottenPasswordViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A79A742241CC3B200CCED5C /* ForgottenPasswordViewModelTests.swift */; }; 6AD8D56A2413848700889F2D /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD8D5612413848700889F2D /* Router.swift */; }; 6AD8D56B2413848700889F2D /* ModalTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD8D5632413848700889F2D /* ModalTransition.swift */; }; 6AD8D56C2413848700889F2D /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD8D5642413848700889F2D /* Transition.swift */; }; @@ -107,6 +136,23 @@ 6A46A76D2419052200ED7426 /* ShopTabRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopTabRoute.swift; sourceTree = ""; }; 6A46A76F241905EB00ED7426 /* WishlistTabRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WishlistTabRoute.swift; sourceTree = ""; }; 6A46A772241908F400ED7426 /* DependencyGraph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencyGraph.swift; sourceTree = ""; }; + 6A5E44F5241C9A7A001CA6C0 /* TransitionMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransitionMock.swift; sourceTree = ""; }; + 6A5E44FD241C9CB0001CA6C0 /* DependencyContainerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencyContainerMock.swift; sourceTree = ""; }; + 6A5E4500241C9D3B001CA6C0 /* LoginViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModelMock.swift; sourceTree = ""; }; + 6A5E4502241C9DA1001CA6C0 /* ForgottenPasswordViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForgottenPasswordViewModelMock.swift; sourceTree = ""; }; + 6A5E4504241C9E1F001CA6C0 /* SignUpViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpViewModelMock.swift; sourceTree = ""; }; + 6A5E4506241C9E70001CA6C0 /* PopUpViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpViewModelMock.swift; sourceTree = ""; }; + 6A5E4508241C9EBC001CA6C0 /* ProductViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductViewModelMock.swift; sourceTree = ""; }; + 6A5E450B241C9F81001CA6C0 /* AddVoiceShortcutButtonDelegateMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddVoiceShortcutButtonDelegateMock.swift; sourceTree = ""; }; + 6A5E450D241C9FB7001CA6C0 /* ShopViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopViewModelMock.swift; sourceTree = ""; }; + 6A5E450F241C9FE6001CA6C0 /* WishlistViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WishlistViewModelMock.swift; sourceTree = ""; }; + 6A5E4512241CA1E6001CA6C0 /* LoginRouteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginRouteTests.swift; sourceTree = ""; }; + 6A5E4514241CA48E001CA6C0 /* RouterMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterMock.swift; sourceTree = ""; }; + 6A5E4517241CAD67001CA6C0 /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; + 6A5E4519241CAEC7001CA6C0 /* PopUpRouteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpRouteTests.swift; sourceTree = ""; }; + 6A5E451B241CAF97001CA6C0 /* ShopTabRouteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopTabRouteTests.swift; sourceTree = ""; }; + 6A5E451D241CB002001CA6C0 /* WishlistTabRouteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WishlistTabRouteTests.swift; sourceTree = ""; }; + 6A5E4520241CB055001CA6C0 /* DefaultRouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultRouterTests.swift; sourceTree = ""; }; 6A61167E24136AE30099C25F /* RoutingExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RoutingExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6A61168124136AE30099C25F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 6A61168324136AE30099C25F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -115,7 +161,6 @@ 6A61168F24136AE50099C25F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 6A61169124136AE50099C25F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6A61169624136AE50099C25F /* RoutingExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RoutingExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 6A61169A24136AE50099C25F /* RoutingExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutingExampleTests.swift; sourceTree = ""; }; 6A61169C24136AE50099C25F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6A6116A524136C5C0099C25F /* MainTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = ""; }; 6A6116A724136EF30099C25F /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; @@ -126,6 +171,19 @@ 6A6116B824136FFD0099C25F /* WishlistViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WishlistViewController.swift; sourceTree = ""; }; 6A6116BC241372270099C25F /* LayoutHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutHelper.swift; sourceTree = ""; }; 6A6116BE241372C70099C25F /* DefaultButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultButton.swift; sourceTree = ""; }; + 6A79A729241CB55B00CCED5C /* ViewControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerMock.swift; sourceTree = ""; }; + 6A79A72B241CB67300CCED5C /* DeeplinkRouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeeplinkRouterTests.swift; sourceTree = ""; }; + 6A79A72D241CBC5900CCED5C /* ProductRouteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductRouteTests.swift; sourceTree = ""; }; + 6A79A72F241CBCB900CCED5C /* ForgottenPasswordRouteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForgottenPasswordRouteTests.swift; sourceTree = ""; }; + 6A79A731241CBD2F00CCED5C /* SignUpRouteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpRouteTests.swift; sourceTree = ""; }; + 6A79A734241CBDAB00CCED5C /* ShopViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopViewModelTests.swift; sourceTree = ""; }; + 6A79A736241CBFD100CCED5C /* RoutesMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutesMock.swift; sourceTree = ""; }; + 6A79A738241CC17C00CCED5C /* WishlistViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WishlistViewModelTests.swift; sourceTree = ""; }; + 6A79A73A241CC1A600CCED5C /* ProductViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductViewModelTests.swift; sourceTree = ""; }; + 6A79A73C241CC23D00CCED5C /* PopUpViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpViewModelTests.swift; sourceTree = ""; }; + 6A79A73E241CC2EC00CCED5C /* SignUpViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpViewModelTests.swift; sourceTree = ""; }; + 6A79A740241CC36F00CCED5C /* LoginViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModelTests.swift; sourceTree = ""; }; + 6A79A742241CC3B200CCED5C /* ForgottenPasswordViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForgottenPasswordViewModelTests.swift; sourceTree = ""; }; 6AD8D5612413848700889F2D /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; 6AD8D5632413848700889F2D /* ModalTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalTransition.swift; sourceTree = ""; }; 6AD8D5642413848700889F2D /* Transition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Transition.swift; sourceTree = ""; }; @@ -206,6 +264,90 @@ path = Tabs; sourceTree = ""; }; + 6A5E44E2241C95DA001CA6C0 /* Mocks */ = { + isa = PBXGroup; + children = ( + 6A5E4514241CA48E001CA6C0 /* RouterMock.swift */, + 6A79A736241CBFD100CCED5C /* RoutesMock.swift */, + 6A5E450A241C9F78001CA6C0 /* Others */, + 6A5E44F4241C9A6E001CA6C0 /* Transitions */, + 6A5E44FF241C9D2E001CA6C0 /* ViewModels */, + ); + path = Mocks; + sourceTree = ""; + }; + 6A5E44F4241C9A6E001CA6C0 /* Transitions */ = { + isa = PBXGroup; + children = ( + 6A5E44F5241C9A7A001CA6C0 /* TransitionMock.swift */, + ); + path = Transitions; + sourceTree = ""; + }; + 6A5E44FC241C9C98001CA6C0 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 6A5E44FD241C9CB0001CA6C0 /* DependencyContainerMock.swift */, + 6A5E4516241CAD59001CA6C0 /* Extensions */, + 6A5E44E2241C95DA001CA6C0 /* Mocks */, + ); + path = "Supporting Files"; + sourceTree = ""; + }; + 6A5E44FF241C9D2E001CA6C0 /* ViewModels */ = { + isa = PBXGroup; + children = ( + 6A5E4500241C9D3B001CA6C0 /* LoginViewModelMock.swift */, + 6A5E4502241C9DA1001CA6C0 /* ForgottenPasswordViewModelMock.swift */, + 6A5E4504241C9E1F001CA6C0 /* SignUpViewModelMock.swift */, + 6A5E4506241C9E70001CA6C0 /* PopUpViewModelMock.swift */, + 6A5E4508241C9EBC001CA6C0 /* ProductViewModelMock.swift */, + 6A5E450D241C9FB7001CA6C0 /* ShopViewModelMock.swift */, + 6A5E450F241C9FE6001CA6C0 /* WishlistViewModelMock.swift */, + ); + path = ViewModels; + sourceTree = ""; + }; + 6A5E450A241C9F78001CA6C0 /* Others */ = { + isa = PBXGroup; + children = ( + 6A5E450B241C9F81001CA6C0 /* AddVoiceShortcutButtonDelegateMock.swift */, + 6A79A729241CB55B00CCED5C /* ViewControllerMock.swift */, + ); + path = Others; + sourceTree = ""; + }; + 6A5E4511241CA1D6001CA6C0 /* Routes Tests */ = { + isa = PBXGroup; + children = ( + 6A79A72F241CBCB900CCED5C /* ForgottenPasswordRouteTests.swift */, + 6A5E4512241CA1E6001CA6C0 /* LoginRouteTests.swift */, + 6A5E4519241CAEC7001CA6C0 /* PopUpRouteTests.swift */, + 6A79A72D241CBC5900CCED5C /* ProductRouteTests.swift */, + 6A5E451B241CAF97001CA6C0 /* ShopTabRouteTests.swift */, + 6A79A731241CBD2F00CCED5C /* SignUpRouteTests.swift */, + 6A5E451D241CB002001CA6C0 /* WishlistTabRouteTests.swift */, + ); + path = "Routes Tests"; + sourceTree = ""; + }; + 6A5E4516241CAD59001CA6C0 /* Extensions */ = { + isa = PBXGroup; + children = ( + 6A5E4517241CAD67001CA6C0 /* UIViewController.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 6A5E451F241CB047001CA6C0 /* Routers Tests */ = { + isa = PBXGroup; + children = ( + 6A5E4520241CB055001CA6C0 /* DefaultRouterTests.swift */, + 6A79A72B241CB67300CCED5C /* DeeplinkRouterTests.swift */, + ); + path = "Routers Tests"; + sourceTree = ""; + }; 6A61167524136AE30099C25F = { isa = PBXGroup; children = ( @@ -251,8 +393,11 @@ 6A61169924136AE50099C25F /* RoutingExampleTests */ = { isa = PBXGroup; children = ( - 6A61169A24136AE50099C25F /* RoutingExampleTests.swift */, 6A61169C24136AE50099C25F /* Info.plist */, + 6A5E451F241CB047001CA6C0 /* Routers Tests */, + 6A5E4511241CA1D6001CA6C0 /* Routes Tests */, + 6A5E44FC241C9C98001CA6C0 /* Supporting Files */, + 6A79A733241CBD9700CCED5C /* ViewModels Tests */, ); path = RoutingExampleTests; sourceTree = ""; @@ -310,6 +455,20 @@ path = Utils; sourceTree = ""; }; + 6A79A733241CBD9700CCED5C /* ViewModels Tests */ = { + isa = PBXGroup; + children = ( + 6A79A742241CC3B200CCED5C /* ForgottenPasswordViewModelTests.swift */, + 6A79A740241CC36F00CCED5C /* LoginViewModelTests.swift */, + 6A79A73C241CC23D00CCED5C /* PopUpViewModelTests.swift */, + 6A79A73A241CC1A600CCED5C /* ProductViewModelTests.swift */, + 6A79A734241CBDAB00CCED5C /* ShopViewModelTests.swift */, + 6A79A73E241CC2EC00CCED5C /* SignUpViewModelTests.swift */, + 6A79A738241CC17C00CCED5C /* WishlistViewModelTests.swift */, + ); + path = "ViewModels Tests"; + sourceTree = ""; + }; 6AD8D55F2413848700889F2D /* Routing */ = { isa = PBXGroup; children = ( @@ -611,7 +770,36 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6A61169B24136AE50099C25F /* RoutingExampleTests.swift in Sources */, + 6A79A741241CC36F00CCED5C /* LoginViewModelTests.swift in Sources */, + 6A79A730241CBCB900CCED5C /* ForgottenPasswordRouteTests.swift in Sources */, + 6A79A732241CBD2F00CCED5C /* SignUpRouteTests.swift in Sources */, + 6A5E44FE241C9CB0001CA6C0 /* DependencyContainerMock.swift in Sources */, + 6A5E4509241C9EBC001CA6C0 /* ProductViewModelMock.swift in Sources */, + 6A5E450E241C9FB7001CA6C0 /* ShopViewModelMock.swift in Sources */, + 6A5E4501241C9D3B001CA6C0 /* LoginViewModelMock.swift in Sources */, + 6A5E450C241C9F81001CA6C0 /* AddVoiceShortcutButtonDelegateMock.swift in Sources */, + 6A79A73D241CC23D00CCED5C /* PopUpViewModelTests.swift in Sources */, + 6A79A739241CC17C00CCED5C /* WishlistViewModelTests.swift in Sources */, + 6A5E44F6241C9A7A001CA6C0 /* TransitionMock.swift in Sources */, + 6A79A735241CBDAB00CCED5C /* ShopViewModelTests.swift in Sources */, + 6A5E451E241CB002001CA6C0 /* WishlistTabRouteTests.swift in Sources */, + 6A5E4515241CA48E001CA6C0 /* RouterMock.swift in Sources */, + 6A79A72A241CB55B00CCED5C /* ViewControllerMock.swift in Sources */, + 6A79A72E241CBC5900CCED5C /* ProductRouteTests.swift in Sources */, + 6A5E4513241CA1E6001CA6C0 /* LoginRouteTests.swift in Sources */, + 6A5E451C241CAF97001CA6C0 /* ShopTabRouteTests.swift in Sources */, + 6A79A73F241CC2EC00CCED5C /* SignUpViewModelTests.swift in Sources */, + 6A79A72C241CB67300CCED5C /* DeeplinkRouterTests.swift in Sources */, + 6A5E4505241C9E1F001CA6C0 /* SignUpViewModelMock.swift in Sources */, + 6A5E4518241CAD67001CA6C0 /* UIViewController.swift in Sources */, + 6A5E4503241C9DA1001CA6C0 /* ForgottenPasswordViewModelMock.swift in Sources */, + 6A5E4521241CB055001CA6C0 /* DefaultRouterTests.swift in Sources */, + 6A5E4510241C9FE7001CA6C0 /* WishlistViewModelMock.swift in Sources */, + 6A79A73B241CC1A600CCED5C /* ProductViewModelTests.swift in Sources */, + 6A79A743241CC3B200CCED5C /* ForgottenPasswordViewModelTests.swift in Sources */, + 6A5E451A241CAEC7001CA6C0 /* PopUpRouteTests.swift in Sources */, + 6A5E4507241C9E70001CA6C0 /* PopUpViewModelMock.swift in Sources */, + 6A79A737241CBFD100CCED5C /* RoutesMock.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/RoutingExample.xcodeproj/xcshareddata/xcschemes/RoutingExample.xcscheme b/RoutingExample.xcodeproj/xcshareddata/xcschemes/RoutingExample.xcscheme index 02a0a8c..53d5c8b 100644 --- a/RoutingExample.xcodeproj/xcshareddata/xcschemes/RoutingExample.xcscheme +++ b/RoutingExample.xcodeproj/xcshareddata/xcschemes/RoutingExample.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/RoutingExampleTests/Routers Tests/DeeplinkRouterTests.swift b/RoutingExampleTests/Routers Tests/DeeplinkRouterTests.swift new file mode 100644 index 0000000..89607a6 --- /dev/null +++ b/RoutingExampleTests/Routers Tests/DeeplinkRouterTests.swift @@ -0,0 +1,144 @@ +// +// DeeplinkRouterTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +import DependencyContainer +@testable import RoutingExample + +final class DeeplinkRouterTests: XCTestCase { + let root = ViewControllerMock() + + // MARK: - AppRoutes tests + + func testProductRoute() { + XCTAssertEqual(AppRoutes(url: URL(string: "www.routing.com/product?something=meh")!), AppRoutes.product) + } + + func testLoginRoute() { + XCTAssertEqual(AppRoutes(url: URL(string: "www.routing.com/login?something=meh")!), AppRoutes.login) + } + + func testSignUpRoute() { + XCTAssertEqual(AppRoutes(url: URL(string: "www.routing.com/sign-up?something=meh")!), AppRoutes.signUp) + } + + func testWishlistUpRoute() { + XCTAssertEqual(AppRoutes(url: URL(string: "www.routing.com/wishlist?something=meh")!), AppRoutes.wishlist) + } + + func testShopUpRoute() { + XCTAssertEqual(AppRoutes(url: URL(string: "www.routing.com/shop?something=meh")!), AppRoutes.shop) + } + + func testInvalidRoute() { + XCTAssertNil(AppRoutes(url: URL(string: "www.routing.com")!)) + } +} + +extension DeeplinkRouterTests { + // MARK: - Route to url tests + + func testProductDeeplinkRoute() { + let transition = TransitionMock() + let router = DeeplinkRouter.makeDeeplink() + router.root = root + + let result = router.route(to: URL(string: "www.routing.com/product?something=meh")!, as: transition) + + XCTAssertTrue(result) + XCTAssertEqual(transition.openViewControllerCallsCount, 1) + XCTAssertTrue(transition.openViewControllerReceivedVC is ProductViewController) + XCTAssertTrue(transition.openViewControllerReceivedFromVC === root) + XCTAssertNil(transition.openViewControllerReceivedCompletion) + } + + func testLoginDeeplinkRoute() { + let transition = TransitionMock() + let router = DeeplinkRouter.makeDeeplink() + router.root = root + + let result = router.route(to: URL(string: "www.routing.com/login?something=meh")!, as: transition) + + XCTAssertTrue(result) + XCTAssertEqual(transition.openViewControllerCallsCount, 1) + XCTAssertTrue(transition.openViewControllerReceivedVC?.asNavigationsRootVC() is LoginViewController) + XCTAssertTrue(transition.openViewControllerReceivedFromVC === root) + XCTAssertNil(transition.openViewControllerReceivedCompletion) + } + + func testSignUpDeeplinkRoute() { + let transition = TransitionMock() + let router = DeeplinkRouter.makeDeeplink() + router.root = root + + let result = router.route(to: URL(string: "www.routing.com/sign-up?something=meh")!, as: transition) + + XCTAssertTrue(result) + XCTAssertEqual(transition.openViewControllerCallsCount, 1) + XCTAssertTrue(transition.openViewControllerReceivedVC is SignUpViewController) + XCTAssertTrue(transition.openViewControllerReceivedFromVC === root) + XCTAssertNil(transition.openViewControllerReceivedCompletion) + } + + func testWishlistDeeplinkRoute() { + let tabbar = UITabBarController() + tabbar.viewControllers = [UIViewController(), UIViewController(), UIViewController()] + root.underlyingTabBarController = tabbar + tabbar.selectedIndex = 2 + + let transition = TransitionMock() + let router = DeeplinkRouter.makeDeeplink() + router.root = root + + let result = router.route(to: URL(string: "www.routing.com/wishlist?something=meh")!, as: transition) + + XCTAssertTrue(result) + XCTAssertEqual(transition.openViewControllerCallsCount, 0, "Nothing was opened") + XCTAssertEqual(root.tabBarController?.selectedIndex, 1, "Wishlist is the index 1") + } + + func testShopDeeplinkRoute() { + let tabbar = UITabBarController() + tabbar.viewControllers = [UIViewController(), UIViewController(), UIViewController()] + root.underlyingTabBarController = tabbar + tabbar.selectedIndex = 2 + + let transition = TransitionMock() + let router = DeeplinkRouter.makeDeeplink() + router.root = root + + let result = router.route(to: URL(string: "www.routing.com/shop?something=meh")!, as: transition) + + XCTAssertTrue(result) + XCTAssertEqual(transition.openViewControllerCallsCount, 0, "Nothing was opened") + XCTAssertEqual(root.tabBarController?.selectedIndex, 0, "Shop is the index 0") + } + + func testInvalidDeeplinkRoute() { + let tabbar = UITabBarController() + tabbar.viewControllers = [UIViewController(), UIViewController(), UIViewController()] + root.underlyingTabBarController = tabbar + tabbar.selectedIndex = 2 + + let transition = TransitionMock() + let router = DeeplinkRouter.makeDeeplink() + router.root = root + + let result = router.route(to: URL(string: "www.routing.com")!, as: transition) + + XCTAssertFalse(result) + } +} + +extension DeeplinkRouter { + /// Helper method that injects mocked value by default. + static func makeDeeplink(rootTransition: Transition = TransitionMock(), + container: DependencyContainer = DependencyContainer.mock()) -> DeeplinkRouter { + return DeeplinkRouter(rootTransition: rootTransition, container: container) + } +} diff --git a/RoutingExampleTests/Routers Tests/DefaultRouterTests.swift b/RoutingExampleTests/Routers Tests/DefaultRouterTests.swift new file mode 100644 index 0000000..8380ede --- /dev/null +++ b/RoutingExampleTests/Routers Tests/DefaultRouterTests.swift @@ -0,0 +1,174 @@ +// +// DefaultRouterTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +import DependencyContainer +@testable import RoutingExample + +final class DefaultRouterTests: XCTestCase { + let root = ViewControllerMock() + + // MARK: - Open methods + + func testRouteToNotOpeningAnythingWhenRootIsNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault() + + router.route(to: UIViewController(), as: transition) + + XCTAssertNil(router.root) + XCTAssertEqual(transition.openViewControllerCallsCount, 0, "Nothing is opened if root is nil") + } + + func testRouteToOpeningControllerWhenRootIsNotNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault() + router.root = root + + let toBePresented = UIViewController() + router.route(to: toBePresented, as: transition) + + XCTAssertNotNil(router.root) + XCTAssertEqual(transition.openViewControllerCallsCount, 1) + XCTAssertTrue(transition.openViewControllerReceivedVC === toBePresented) + XCTAssertTrue(transition.openViewControllerReceivedFromVC === root) + } + + func testRouteToCompletionNotOpeningAnythingWhenRootIsNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault() + + router.route(to: UIViewController(), as: transition, completion: { print("yay") }) + + XCTAssertNil(router.root) + XCTAssertEqual(transition.openViewControllerCallsCount, 0, "Nothing is opened if root is nil") + } + + func testRouteToCompletionOpeningControllerWhenRootIsNotNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault() + router.root = root + + + let toBePresented = UIViewController() + router.route(to: toBePresented, as: transition, completion: { print("yay") }) + + XCTAssertNotNil(router.root) + XCTAssertEqual(transition.openViewControllerCallsCount, 1) + XCTAssertTrue(transition.openViewControllerReceivedVC === toBePresented) + XCTAssertTrue(transition.openViewControllerReceivedFromVC === root) + XCTAssertNotNil(transition.openViewControllerReceivedCompletion) + } +} + +extension DefaultRouterTests { + // MARK: - Close methods + + func testCloseNotClosingAnythingWhenRootIsNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault(rootTransition: transition) + + router.close() + + XCTAssertNil(router.root) + XCTAssertEqual(transition.closeViewControllerCallsCount, 0, "Nothing was closed if root is nil") + } + + func testCloseClosesControllerWhenRootIsNotNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault(rootTransition: transition) + router.root = root + + router.close() + + XCTAssertNotNil(router.root) + XCTAssertEqual(transition.closeViewControllerCallsCount, 1) + XCTAssertTrue(transition.closeViewControllerReceivedVC === root) + } + + func testCloseCompletionNotClosingAnythingWhenRootIsNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault(rootTransition: transition) + + router.close(completion: { print("yay") }) + + XCTAssertNil(router.root) + XCTAssertEqual(transition.closeViewControllerCallsCount, 0, "Nothing was closed if root is nil") + } + + func testCloseCompletionClosingControllerWhenRootIsNotNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault(rootTransition: transition) + router.root = root + + router.close(completion: { print("yay") }) + + XCTAssertNotNil(router.root) + XCTAssertEqual(transition.closeViewControllerCallsCount, 1) + XCTAssertTrue(transition.closeViewControllerReceivedVC === root) + XCTAssertNotNil(transition.closeViewControllerReceivedCompletion) + } +} + +extension DefaultRouterTests { + // MARK: - Dismiss methods + + func testDismissNotDismissingAnythingWhenRootIsNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault(rootTransition: transition) + + router.dismiss() + + XCTAssertNil(router.root) + XCTAssertEqual(root.dismissWithAnimatedCalledCount, 0, "Nothing was closed if root is nil") + } + + func testDismissClosesControllerWhenRootIsNotNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault(rootTransition: transition) + router.root = root + + router.dismiss() + + XCTAssertNotNil(router.root) + XCTAssertEqual(root.dismissWithAnimatedCalledCount, 1, "Dismiss calls the root's dismiss directly") + XCTAssertEqual(root.dismissWithAnimatedArguments?.animated, transition.isAnimated) + XCTAssertNil(root.dismissWithAnimatedArguments?.completion) + } + + func testDismissCompletionNotDismissingAnythingWhenRootIsNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault(rootTransition: transition) + + router.dismiss(completion: { print("yay") }) + + XCTAssertNil(router.root) + XCTAssertEqual(root.dismissWithAnimatedCalledCount, 0, "Nothing was closed if root is nil") + } + + func testDismissCompletionDismissingControllerWhenRootIsNotNil() { + let transition = TransitionMock() + let router = DefaultRouter.makeDefault(rootTransition: transition) + router.root = root + + router.dismiss(completion: { print("yay") }) + + XCTAssertNotNil(router.root) + XCTAssertEqual(root.dismissWithAnimatedCalledCount, 1, "Dismiss calls the root's dismiss directly") + XCTAssertEqual(root.dismissWithAnimatedArguments?.animated, transition.isAnimated) + XCTAssertNotNil(root.dismissWithAnimatedArguments?.completion) + } +} + +extension DefaultRouter { + /// Helper method that injects mocked value by default. + static func makeDefault(rootTransition: Transition = TransitionMock(), + container: DependencyContainer = DependencyContainer.mock()) -> DefaultRouter { + return DefaultRouter(rootTransition: rootTransition, container: container) + } +} diff --git a/RoutingExampleTests/Routes Tests/ForgottenPasswordRouteTests.swift b/RoutingExampleTests/Routes Tests/ForgottenPasswordRouteTests.swift new file mode 100644 index 0000000..c35f844 --- /dev/null +++ b/RoutingExampleTests/Routes Tests/ForgottenPasswordRouteTests.swift @@ -0,0 +1,21 @@ +// +// ForgottenPasswordRouteTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class ForgottenPasswordRouteTests: XCTestCase { + func testOpenForgottenPassword() { + let router = RouterMock() + router.openForgottenPassword() + + XCTAssertEqual(router.routeToCallsCount, 1) + XCTAssertTrue(router.routeToReceivedVC is ForgottenPasswordViewController, "The screen was presented as a ForgottenPasswordVC") + XCTAssertTrue(router.routeToReceivedTransition is PushTransition, "The screen was presented with a Push Transition") + } +} diff --git a/RoutingExampleTests/Routes Tests/LoginRouteTests.swift b/RoutingExampleTests/Routes Tests/LoginRouteTests.swift new file mode 100644 index 0000000..b3580f3 --- /dev/null +++ b/RoutingExampleTests/Routes Tests/LoginRouteTests.swift @@ -0,0 +1,21 @@ +// +// LoginRouteTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class LoginRouteTests: XCTestCase { + func testOpenLogin() { + let router = RouterMock() + router.openLogin() + + XCTAssertEqual(router.routeToCallsCount, 1) + XCTAssertTrue(router.routeToReceivedVC?.asNavigationsRootVC() is LoginViewController, "The screen was presented embedded in a Navigation Controller") + XCTAssertTrue(router.routeToReceivedTransition is ModalTransition, "The screen was presented with a Modal Transition") + } +} diff --git a/RoutingExampleTests/Routes Tests/PopUpRouteTests.swift b/RoutingExampleTests/Routes Tests/PopUpRouteTests.swift new file mode 100644 index 0000000..4422a44 --- /dev/null +++ b/RoutingExampleTests/Routes Tests/PopUpRouteTests.swift @@ -0,0 +1,22 @@ +// +// PopUpRouteTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class PopUpRouteTests: XCTestCase { + func testOpenPopUp() { + let router = RouterMock() + router.openPopUp(withMessage: "Something!") + + XCTAssertEqual(router.routeToCallsCount, 1) + XCTAssertTrue(router.routeToReceivedVC is PopUpViewController, "The screen was presented with a PopUpViewController") + XCTAssertTrue(router.routeToReceivedTransition is AnimatedTransition, "The screen was presented with an Animated Transition") + XCTAssertTrue((router.routeToReceivedTransition as? AnimatedTransition)?.animatedTransition is FadeAnimatedTransitioning, "The Animated Transition is a Fade animation") + } +} diff --git a/RoutingExampleTests/Routes Tests/ProductRouteTests.swift b/RoutingExampleTests/Routes Tests/ProductRouteTests.swift new file mode 100644 index 0000000..1f07bad --- /dev/null +++ b/RoutingExampleTests/Routes Tests/ProductRouteTests.swift @@ -0,0 +1,21 @@ +// +// ProductRouteTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class ProductRouteTests: XCTestCase { + func testOpenProduct() { + let router = RouterMock() + router.openProduct() + + XCTAssertEqual(router.routeToCallsCount, 1) + XCTAssertTrue(router.routeToReceivedVC is ProductViewController, "The screen was presented as a ProductVC") + XCTAssertTrue(router.routeToReceivedTransition is PushTransition, "The screen was presented with a Push Transition") + } +} diff --git a/RoutingExampleTests/Routes Tests/ShopTabRouteTests.swift b/RoutingExampleTests/Routes Tests/ShopTabRouteTests.swift new file mode 100644 index 0000000..aad33fd --- /dev/null +++ b/RoutingExampleTests/Routes Tests/ShopTabRouteTests.swift @@ -0,0 +1,20 @@ +// +// ShopTabRouteTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class ShopTabRouteTests: XCTestCase { + func testMakeShopTab() { + let router = RouterMock() + let result = router.makeShopTab() + + XCTAssertEqual(router.routeToCallsCount, 0, "Nothing was opened") + XCTAssertTrue(result.asNavigationsRootVC() is ShopViewController, "A navigation controller with a ShopVC is returned") + } +} diff --git a/RoutingExampleTests/Routes Tests/SignUpRouteTests.swift b/RoutingExampleTests/Routes Tests/SignUpRouteTests.swift new file mode 100644 index 0000000..a9c5997 --- /dev/null +++ b/RoutingExampleTests/Routes Tests/SignUpRouteTests.swift @@ -0,0 +1,21 @@ +// +// SignUpRouteTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class SignUpRouteTests: XCTestCase { + func testOpenSignUp() { + let router = RouterMock() + router.openSignUp() + + XCTAssertEqual(router.routeToCallsCount, 1) + XCTAssertTrue(router.routeToReceivedVC is SignUpViewController, "The screen was presented as a SignUpVC") + XCTAssertTrue(router.routeToReceivedTransition is PushTransition, "The screen was presented with a Push Transition") + } +} diff --git a/RoutingExampleTests/Routes Tests/WishlistTabRouteTests.swift b/RoutingExampleTests/Routes Tests/WishlistTabRouteTests.swift new file mode 100644 index 0000000..e4f81b0 --- /dev/null +++ b/RoutingExampleTests/Routes Tests/WishlistTabRouteTests.swift @@ -0,0 +1,20 @@ +// +// WishlistTabRouteTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class WishlistTabRouteTests: XCTestCase { + func testMakeWishlistTab() { + let router = RouterMock() + let result = router.makeWishlistTab() + + XCTAssertEqual(router.routeToCallsCount, 0, "Nothing was opened") + XCTAssertTrue(result.asNavigationsRootVC() is WishlistViewController, "A navigation controller with a WishlistVC is returned") + } +} diff --git a/RoutingExampleTests/RoutingExampleTests.swift b/RoutingExampleTests/RoutingExampleTests.swift deleted file mode 100644 index 92d15e4..0000000 --- a/RoutingExampleTests/RoutingExampleTests.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RoutingExampleTests.swift -// RoutingExampleTests -// -// Created by Cassius Pacheco on 7/3/20. -// Copyright © 2020 Cassius Pacheco. All rights reserved. -// - -import XCTest -@testable import RoutingExample - -class RoutingExampleTests: XCTestCase { - - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/RoutingExampleTests/Supporting Files/DependencyContainerMock.swift b/RoutingExampleTests/Supporting Files/DependencyContainerMock.swift new file mode 100644 index 0000000..4b5b9c2 --- /dev/null +++ b/RoutingExampleTests/Supporting Files/DependencyContainerMock.swift @@ -0,0 +1,53 @@ +// +// DependencyContainerMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import Foundation +import DependencyContainer +@testable import RoutingExample + +extension DependencyContainer { + static func mock() -> DependencyContainer { + // Register all interfaces registered in the DependencyGraph.swift with their + // respective Mock classes. This will allow us to control the entire app flow + // with fake data and validate assumptions. + let container = DependencyContainer() + + // Note that's important to keep the same arguments signature here, even + // though they're not being used by the Mock class, because the DependencyContainer + // framework needs them to resolve the dependencies correctly. + container.register(LoginViewModelInterface.self) { (_, routes: LoginViewModel.Routes) in + return LoginViewModelMock() + } + + container.register(ForgottenPasswordViewModelInterface.self) { (_, routes: ForgottenPasswordViewModel.Routes) in + return ForgottenPasswordViewModelMock() + } + + container.register(SignUpViewModelInterface.self) { (_, routes: SignUpViewModel.Routes) in + return SignUpViewModelMock() + } + + container.register(PopUpViewModelInterface.self) { (_, message: String, routes: PopUpViewModel.Routes) in + return PopUpViewModelMock() + } + + container.register(ProductViewModelInterface.self) { (_, routes: ProductViewModel.Routes) in + return ProductViewModelMock() + } + + container.register(ShopViewModelInterface.self) { (_, routes: ShopViewModel.Routes) in + return ShopViewModelMock() + } + + container.register(WishlistViewModelInterface.self) { (_, routes: WishlistViewModel.Routes) in + return WishlistViewModelMock() + } + + return container + } +} diff --git a/RoutingExampleTests/Supporting Files/Extensions/UIViewController.swift b/RoutingExampleTests/Supporting Files/Extensions/UIViewController.swift new file mode 100644 index 0000000..e0bb8df --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Extensions/UIViewController.swift @@ -0,0 +1,17 @@ +// +// UIViewController.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import UIKit + +extension UIViewController { + /// Cast view controller to a `UINavigationViewController` and returns its root view controller. + /// Returns `nil` if `self` isn't a `UINavigationViewController`. + func asNavigationsRootVC() -> UIViewController? { + return (self as? UINavigationController)?.viewControllers.first + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/Others/AddVoiceShortcutButtonDelegateMock.swift b/RoutingExampleTests/Supporting Files/Mocks/Others/AddVoiceShortcutButtonDelegateMock.swift new file mode 100644 index 0000000..4b71ec4 --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/Others/AddVoiceShortcutButtonDelegateMock.swift @@ -0,0 +1,24 @@ +// +// AddVoiceShortcutButtonDelegateMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import Foundation +import IntentsUI +@testable import RoutingExample + +class AddVoiceShortcutButtonDelegateMock: NSObject, INUIAddVoiceShortcutButtonDelegate { + var presentAddVoiceShortcutViewControllerCallsCount = 0 + var presentEditVoiceShortcutViewControllerCallsCount = 0 + + func present(_ addVoiceShortcutViewController: INUIAddVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) { + presentAddVoiceShortcutViewControllerCallsCount += 1 + } + + func present(_ editVoiceShortcutViewController: INUIEditVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) { + presentEditVoiceShortcutViewControllerCallsCount += 1 + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/Others/ViewControllerMock.swift b/RoutingExampleTests/Supporting Files/Mocks/Others/ViewControllerMock.swift new file mode 100644 index 0000000..94ab6e5 --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/Others/ViewControllerMock.swift @@ -0,0 +1,58 @@ +// +// ViewControllerMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import UIKit + +final class ViewControllerMock: UIViewController { + // MARK: - TabBarController + var underlyingTabBarController: UITabBarController? + + override var tabBarController: UITabBarController? { + get { + return underlyingTabBarController + } + } + + // MARK: - PresentedControllers + + private(set) var presentedControllers = [UIViewController]() + + // MARK: - Presented + + var presentWithControllerCalled: Bool { + return presentWithControllerCalledCount > 0 + } + + private(set) var presentWithControllerCalledCount = 0 + private(set) var presentWithControllerArguments: (viewControllerToPresent: UIViewController, animated: Bool, completion: (() -> Void)?)? + + override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) { + presentedControllers.append(viewControllerToPresent) + + presentWithControllerCalledCount += 1 + presentWithControllerArguments = (viewControllerToPresent: viewControllerToPresent, animated: flag, completion: completion) + } + + // MARK: - Dismissed + + private(set) var currentDismissedController: UIViewController? + + var dismissWithAnimatedCalled: Bool { + return dismissWithAnimatedCalledCount > 0 + } + + private(set) var dismissWithAnimatedCalledCount = 0 + private(set) var dismissWithAnimatedArguments: (animated: Bool, completion: (() -> Void)?)? + + override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { + currentDismissedController = presentedControllers.popLast() + + dismissWithAnimatedCalledCount += 1 + dismissWithAnimatedArguments = (animated: flag, completion: completion) + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/RouterMock.swift b/RoutingExampleTests/Supporting Files/Mocks/RouterMock.swift new file mode 100644 index 0000000..d94d05f --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/RouterMock.swift @@ -0,0 +1,79 @@ +// +// RouterMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import UIKit +import DependencyContainer +@testable import RoutingExample + +class RouterMock: NSObject, Router, Closable, Dismissable { + var root: UIViewController? + var container: DependencyContainer + + var routeToCompletionCallsCount = 0 + var routeToCompletionReceivedVC: UIViewController? + var routeToCompletionReceivedTransition: Transition? + var routeToCompletionReceivedCompletion: (() -> Void)? + + var routeToCallsCount = 0 + var routeToReceivedVC: UIViewController? + var routeToReceivedTransition: Transition? + + var closeCompletionCallsCount = 0 + var closeCompletionReceivedCompletion: (() -> Void)? + + var closeCallsCount = 0 + + var dismissCompletionCallsCount = 0 + var dismissCompletionReceivedCompletion: (() -> Void)? + + var dismissCallsCount = 0 + + init(container: DependencyContainer = DependencyContainer.mock()) { + self.container = container + } + + func route(to viewController: UIViewController, as transition: Transition, completion: (() -> Void)?) { + routeToCompletionCallsCount += 1 + routeToCompletionReceivedVC = viewController + routeToCompletionReceivedTransition = transition + routeToCompletionReceivedCompletion = completion + } + + func route(to viewController: UIViewController, as transition: Transition) { + routeToCallsCount += 1 + routeToReceivedVC = viewController + routeToReceivedTransition = transition + } + + func close(completion: (() -> Void)?) { + closeCompletionCallsCount += 1 + closeCompletionReceivedCompletion = completion + } + + func close() { + closeCallsCount += 1 + } + + func dismiss(completion: (() -> Void)?) { + dismissCompletionCallsCount += 1 + dismissCompletionReceivedCompletion = completion + } + + func dismiss() { + dismissCallsCount += 1 + } +} + +// Add all routes in the app as extension +extension RouterMock: PopUpRoute {} +extension RouterMock: ForgottenPasswordRoute {} +extension RouterMock: SignUpRoute {} +extension RouterMock: ProductRoute {} +extension RouterMock: LoginRoute {} +extension RouterMock: ShopTabRoute {} +extension RouterMock: WishlistTabRoute {} diff --git a/RoutingExampleTests/Supporting Files/Mocks/RoutesMock.swift b/RoutingExampleTests/Supporting Files/Mocks/RoutesMock.swift new file mode 100644 index 0000000..53c66a7 --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/RoutesMock.swift @@ -0,0 +1,106 @@ +// +// TestRoutes.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import UIKit +@testable import RoutingExample + +/// `RoutesMock` conforms to all routes protocols in order to validate which routes are being called from the view models. +class RoutesMock: AddVoiceShortcutButtonDelegateMock, ForgottenPasswordRoute, LoginRoute, ProductRoute, PopUpRoute, ShopTabRoute, SignUpRoute, SiriRoute, WishlistTabRoute, Closable, Dismissable { + // MARK: - ForgottenPasswordRoute + var openForgottenPasswordCallsCount = 0 + + func openForgottenPassword() { + openForgottenPasswordCallsCount += 1 + } + + // MARK: - LoginRoute + + var openLoginCallsCount = 0 + + func openLogin() { + openLoginCallsCount += 1 + } + + // MARK: - ProductRoute + + var openProductCallsCount = 0 + + func openProduct() { + openProductCallsCount += 1 + } + + // MARK: - PopUpRoute + + var openPopUpWithMessageCallsCount = 0 + var openPopUpWithMessageReceivedMessage: String? + + func openPopUp(withMessage message: String) { + openPopUpWithMessageCallsCount += 1 + openPopUpWithMessageReceivedMessage = message + } + + // MARK: - ShopTabRoute + + var makeShopTabCallsCount = 0 + var makeShopTabReturnValue: UIViewController! + + func makeShopTab() -> UIViewController { + makeShopTabCallsCount += 1 + return makeShopTabReturnValue + } + + // MARK: - SignUpRoute + + var openSignUpCallsCount = 0 + + func openSignUp() { + openSignUpCallsCount += 1 + } + + // MARK: - WishlistTabRoute + + var makeWishlistTabCallsCount = 0 + var makeWishlistTabReturnValue: UIViewController! + + func makeWishlistTab() -> UIViewController { + makeWishlistTabCallsCount += 1 + return makeWishlistTabReturnValue + } + + // MARK: - Closable + + var closeCallsCount = 0 + + var closeCompletionCallsCount = 0 + var closeCompletionReceivedCompletion: (() -> Void)? + + func close() { + closeCallsCount += 1 + } + + func close(completion: (() -> Void)?) { + closeCompletionCallsCount += 1 + closeCompletionReceivedCompletion = completion + } + + // MARK: - Dismissable + + var dismissCallsCount = 0 + + var dismissCompletionCallsCount = 0 + var dismissCompletionReceivedCompletion: (() -> Void)? + + func dismiss() { + dismissCallsCount += 1 + } + + func dismiss(completion: (() -> Void)?) { + dismissCompletionCallsCount += 1 + dismissCompletionReceivedCompletion = completion + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/Transitions/TransitionMock.swift b/RoutingExampleTests/Supporting Files/Mocks/Transitions/TransitionMock.swift new file mode 100644 index 0000000..45cb694 --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/Transitions/TransitionMock.swift @@ -0,0 +1,38 @@ +// +// TransitionMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import UIKit +@testable import RoutingExample + +class TransitionMock: Transition { + // Received values + var openViewControllerCallsCount = 0 + var openViewControllerReceivedVC: UIViewController? + var openViewControllerReceivedFromVC: UIViewController? + var openViewControllerReceivedCompletion: (() -> Void)? + + var closeViewControllerCallsCount = 0 + var closeViewControllerReceivedVC: UIViewController? + var closeViewControllerReceivedCompletion: (() -> Void)? + + // Protocol implementation + var isAnimated: Bool = true + + func open(_ viewController: UIViewController, from: UIViewController, completion: (() -> Void)?) { + openViewControllerCallsCount += 1 + openViewControllerReceivedVC = viewController + openViewControllerReceivedFromVC = from + openViewControllerReceivedCompletion = completion + } + + func close(_ viewController: UIViewController, completion: (() -> Void)?) { + closeViewControllerCallsCount += 1 + closeViewControllerReceivedVC = viewController + closeViewControllerReceivedCompletion = completion + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/ViewModels/ForgottenPasswordViewModelMock.swift b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/ForgottenPasswordViewModelMock.swift new file mode 100644 index 0000000..fe9f7ba --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/ForgottenPasswordViewModelMock.swift @@ -0,0 +1,18 @@ +// +// ForgottenPasswordViewModelMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import Foundation +@testable import RoutingExample + +class ForgottenPasswordViewModelMock: ForgottenPasswordViewModelInterface { + var resetPasswordButtonTouchUpInsideCallsCount = 0 + + func resetPasswordButtonTouchUpInside() { + resetPasswordButtonTouchUpInsideCallsCount += 1 + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/ViewModels/LoginViewModelMock.swift b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/LoginViewModelMock.swift new file mode 100644 index 0000000..6fea369 --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/LoginViewModelMock.swift @@ -0,0 +1,28 @@ +// +// LoginViewModelMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import Foundation +@testable import RoutingExample + +class LoginViewModelMock: LoginViewModelInterface { + var dismissButtonTouchUpInsideCallsCount = 0 + var forgottenPasswordButtonTouchUpInsideCallsCount = 0 + var signUpButtonTouchUpInsideCallsCount = 0 + + func dismissButtonTouchUpInside() { + dismissButtonTouchUpInsideCallsCount += 1 + } + + func forgottenPasswordButtonTouchUpInside() { + forgottenPasswordButtonTouchUpInsideCallsCount += 1 + } + + func signUpButtonTouchUpInside() { + signUpButtonTouchUpInsideCallsCount += 1 + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/ViewModels/PopUpViewModelMock.swift b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/PopUpViewModelMock.swift new file mode 100644 index 0000000..7a026bd --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/PopUpViewModelMock.swift @@ -0,0 +1,23 @@ +// +// PopUpViewModelMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import Foundation +@testable import RoutingExample + +class PopUpViewModelMock: PopUpViewModelInterface { + var dismissButtonTouchUpInsideCallsCount = 0 + var underlyingMessage: String = "test" + + var message: String { + return underlyingMessage + } + + func dismissButtonTouchUpInside() { + dismissButtonTouchUpInsideCallsCount += 1 + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/ViewModels/ProductViewModelMock.swift b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/ProductViewModelMock.swift new file mode 100644 index 0000000..44d5299 --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/ProductViewModelMock.swift @@ -0,0 +1,31 @@ +// +// ProductViewModelMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import Foundation +import IntentsUI +@testable import RoutingExample + +class ProductViewModelMock: ProductViewModelInterface { + var wishlistButtonTouchUpInsideCallsCount = 0 + var productButtonTouchUpInsideCallsCount = 0 + + // Override this underlying value for custom classes to be returned in `siriButtonDelegate` + var underlyingSiriButtonDelegate: INUIAddVoiceShortcutButtonDelegate = AddVoiceShortcutButtonDelegateMock() + + var siriButtonDelegate: INUIAddVoiceShortcutButtonDelegate { + return underlyingSiriButtonDelegate + } + + func productButtonTouchUpInside() { + productButtonTouchUpInsideCallsCount += 1 + } + + func wishlistButtonTouchUpInside() { + wishlistButtonTouchUpInsideCallsCount += 1 + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/ViewModels/ShopViewModelMock.swift b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/ShopViewModelMock.swift new file mode 100644 index 0000000..3a42348 --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/ShopViewModelMock.swift @@ -0,0 +1,18 @@ +// +// ShopViewModelMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import Foundation +@testable import RoutingExample + +class ShopViewModelMock: ShopViewModelInterface { + var productButtonTouchUpInsideCallsCount = 0 + + func productButtonTouchUpInside() { + productButtonTouchUpInsideCallsCount += 1 + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/ViewModels/SignUpViewModelMock.swift b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/SignUpViewModelMock.swift new file mode 100644 index 0000000..a51ddaf --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/SignUpViewModelMock.swift @@ -0,0 +1,23 @@ +// +// SignUpViewModelMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import Foundation +@testable import RoutingExample + +class SignUpViewModelMock: SignUpViewModelInterface { + var forgottenPasswordButtonTouchUpInsideCallsCount = 0 + var dismissButtonTouchUpInsideCallsCount = 0 + + func dismissButtonTouchUpInside() { + dismissButtonTouchUpInsideCallsCount += 1 + } + + func forgottenPasswordButtonTouchUpInside() { + forgottenPasswordButtonTouchUpInsideCallsCount += 1 + } +} diff --git a/RoutingExampleTests/Supporting Files/Mocks/ViewModels/WishlistViewModelMock.swift b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/WishlistViewModelMock.swift new file mode 100644 index 0000000..23a1e44 --- /dev/null +++ b/RoutingExampleTests/Supporting Files/Mocks/ViewModels/WishlistViewModelMock.swift @@ -0,0 +1,23 @@ +// +// WishlistViewModelMock.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import Foundation +@testable import RoutingExample + +class WishlistViewModelMock: WishlistViewModelInterface { + var productButtonTouchUpInsideCallsCount = 0 + var loginButtonTouchUpInsideCallsCount = 0 + + func productButtonTouchUpInside() { + productButtonTouchUpInsideCallsCount += 1 + } + + func loginButtonTouchUpInside() { + loginButtonTouchUpInsideCallsCount += 1 + } +} diff --git a/RoutingExampleTests/ViewModels Tests/ForgottenPasswordViewModelTests.swift b/RoutingExampleTests/ViewModels Tests/ForgottenPasswordViewModelTests.swift new file mode 100644 index 0000000..f5f4c39 --- /dev/null +++ b/RoutingExampleTests/ViewModels Tests/ForgottenPasswordViewModelTests.swift @@ -0,0 +1,22 @@ +// +// ForgottenPasswordViewModelTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class ForgottenPasswordViewModelTests: XCTestCase { + func testResetPasswordButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = ForgottenPasswordViewModel(router: routes) + + viewModel.resetPasswordButtonTouchUpInside() + + XCTAssertEqual(routes.openPopUpWithMessageCallsCount, 1) + XCTAssertEqual(routes.openPopUpWithMessageReceivedMessage, "E-mail sent") + } +} diff --git a/RoutingExampleTests/ViewModels Tests/LoginViewModelTests.swift b/RoutingExampleTests/ViewModels Tests/LoginViewModelTests.swift new file mode 100644 index 0000000..1dc191e --- /dev/null +++ b/RoutingExampleTests/ViewModels Tests/LoginViewModelTests.swift @@ -0,0 +1,39 @@ +// +// LoginViewModelTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class LoginViewModelTests: XCTestCase { + func testDismissButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = LoginViewModel(router: routes) + + viewModel.dismissButtonTouchUpInside() + + XCTAssertEqual(routes.closeCallsCount, 1) + } + + func testForgottenPasswordButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = LoginViewModel(router: routes) + + viewModel.forgottenPasswordButtonTouchUpInside() + + XCTAssertEqual(routes.openForgottenPasswordCallsCount, 1) + } + + func testSignUpButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = LoginViewModel(router: routes) + + viewModel.signUpButtonTouchUpInside() + + XCTAssertEqual(routes.openSignUpCallsCount, 1) + } +} diff --git a/RoutingExampleTests/ViewModels Tests/PopUpViewModelTests.swift b/RoutingExampleTests/ViewModels Tests/PopUpViewModelTests.swift new file mode 100644 index 0000000..424ece5 --- /dev/null +++ b/RoutingExampleTests/ViewModels Tests/PopUpViewModelTests.swift @@ -0,0 +1,26 @@ +// +// PopUpViewModelTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class PopUpViewModelTests: XCTestCase { + func testDismissButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = PopUpViewModel(message: "something", router: routes) + + viewModel.dismissButtonTouchUpInside() + + XCTAssertEqual(routes.closeCallsCount, 1) + } + + func testViewModelMessage() { + let viewModel = PopUpViewModel(message: "something", router: RoutesMock()) + XCTAssertEqual(viewModel.message, "something") + } +} diff --git a/RoutingExampleTests/ViewModels Tests/ProductViewModelTests.swift b/RoutingExampleTests/ViewModels Tests/ProductViewModelTests.swift new file mode 100644 index 0000000..bc09290 --- /dev/null +++ b/RoutingExampleTests/ViewModels Tests/ProductViewModelTests.swift @@ -0,0 +1,38 @@ +// +// ProductViewModelTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class ProductViewModelTests: XCTestCase { + func testProductButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = ProductViewModel(router: routes) + + viewModel.productButtonTouchUpInside() + + XCTAssertEqual(routes.openProductCallsCount, 1) + } + + func testWishlistButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = ProductViewModel(router: routes) + + viewModel.wishlistButtonTouchUpInside() + + XCTAssertEqual(routes.openPopUpWithMessageCallsCount, 1) + XCTAssertEqual(routes.openPopUpWithMessageReceivedMessage, "Product added to the Wishlist!") + } + + func testSiriButtonDelegate() { + let routes = RoutesMock() + let viewModel = ProductViewModel(router: routes) + + XCTAssertTrue(viewModel.siriButtonDelegate === routes) + } +} diff --git a/RoutingExampleTests/ViewModels Tests/ShopViewModelTests.swift b/RoutingExampleTests/ViewModels Tests/ShopViewModelTests.swift new file mode 100644 index 0000000..d637a3d --- /dev/null +++ b/RoutingExampleTests/ViewModels Tests/ShopViewModelTests.swift @@ -0,0 +1,21 @@ +// +// ShopViewModelTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class ShopViewModelTests: XCTestCase { + func testProductButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = ShopViewModel(router: routes) + + viewModel.productButtonTouchUpInside() + + XCTAssertEqual(routes.openProductCallsCount, 1) + } +} diff --git a/RoutingExampleTests/ViewModels Tests/SignUpViewModelTests.swift b/RoutingExampleTests/ViewModels Tests/SignUpViewModelTests.swift new file mode 100644 index 0000000..b422a3c --- /dev/null +++ b/RoutingExampleTests/ViewModels Tests/SignUpViewModelTests.swift @@ -0,0 +1,30 @@ +// +// SignUpViewModelTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class SignUpViewModelTests: XCTestCase { + func testDismissButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = SignUpViewModel(router: routes) + + viewModel.dismissButtonTouchUpInside() + + XCTAssertEqual(routes.dismissCallsCount, 1) + } + + func testForgottenPasswordButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = SignUpViewModel(router: routes) + + viewModel.forgottenPasswordButtonTouchUpInside() + + XCTAssertEqual(routes.openForgottenPasswordCallsCount, 1) + } +} diff --git a/RoutingExampleTests/ViewModels Tests/WishlistViewModelTests.swift b/RoutingExampleTests/ViewModels Tests/WishlistViewModelTests.swift new file mode 100644 index 0000000..a2baf20 --- /dev/null +++ b/RoutingExampleTests/ViewModels Tests/WishlistViewModelTests.swift @@ -0,0 +1,30 @@ +// +// WishlistViewModelTests.swift +// RoutingExampleTests +// +// Created by Cassius Pacheco on 14/3/20. +// Copyright © 2020 Cassius Pacheco. All rights reserved. +// + +import XCTest +@testable import RoutingExample + +final class WishlistViewModelTests: XCTestCase { + func testProductButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = WishlistViewModel(router: routes) + + viewModel.productButtonTouchUpInside() + + XCTAssertEqual(routes.openProductCallsCount, 1) + } + + func testLoginButtonTouchUpInside() { + let routes = RoutesMock() + let viewModel = WishlistViewModel(router: routes) + + viewModel.loginButtonTouchUpInside() + + XCTAssertEqual(routes.openLoginCallsCount, 1) + } +}