diff --git a/LionHeart-iOS/LionHeart-iOS.xcodeproj/project.pbxproj b/LionHeart-iOS/LionHeart-iOS.xcodeproj/project.pbxproj index 1f0c8bac..e31d19f8 100644 --- a/LionHeart-iOS/LionHeart-iOS.xcodeproj/project.pbxproj +++ b/LionHeart-iOS/LionHeart-iOS.xcodeproj/project.pbxproj @@ -120,7 +120,8 @@ B59BFD3F2ADBBF2B005D2D81 /* CurriculumFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59BFD3E2ADBBF2B005D2D81 /* CurriculumFactory.swift */; }; B59BFD412ADBBFB2005D2D81 /* MyPageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59BFD402ADBBFB2005D2D81 /* MyPageFactory.swift */; }; B59BFD432ADBBFF5005D2D81 /* ArticleFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59BFD422ADBBFF5005D2D81 /* ArticleFactory.swift */; }; - B5BE51C12B15B8F100042EF3 /* ServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BE51C02B15B8F100042EF3 /* ServiceTests.swift */; }; + B59FC6BD2B19C569000996CA /* CurriculumArticleByWeekHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4DB30BB2A61691F00413EB9 /* CurriculumArticleByWeekHeaderView.swift */; }; + B5BE51C12B15B8F100042EF3 /* ChallengeServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BE51C02B15B8F100042EF3 /* ChallengeServiceTests.swift */; }; B5BE51C72B15C75F00042EF3 /* URLSessionStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BE51C62B15C75F00042EF3 /* URLSessionStub.swift */; }; B5BE51C92B15CB9600042EF3 /* JSONLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BE51C82B15CB9600042EF3 /* JSONLoader.swift */; }; B5C6A2B22A5DB0B10021BE5E /* ArticleDetailTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C6A2B12A5DB0B10021BE5E /* ArticleDetailTableView.swift */; }; @@ -192,7 +193,6 @@ C034EDD82ADE21D200AD6FF3 /* SplashAdaptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C034EDD72ADE21D200AD6FF3 /* SplashAdaptor.swift */; }; C034EDDA2ADE2A0E00AD6FF3 /* AuthCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C034EDD92ADE2A0E00AD6FF3 /* AuthCoordinator.swift */; }; C034EDDC2ADE2A2C00AD6FF3 /* AuthAdaptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C034EDDB2ADE2A2C00AD6FF3 /* AuthAdaptor.swift */; }; - C034EDE02ADE3A4A00AD6FF3 /* LionHeart_iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B532E8482A5525C800F0DB19 /* LionHeart_iOSTests.swift */; }; C034EE1E2ADE44E200AD6FF3 /* ArticleCategoryAdaptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C034EE1D2ADE44E200AD6FF3 /* ArticleCategoryAdaptor.swift */; }; C034EE202ADE450600AD6FF3 /* ArticleCategoryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C034EE1F2ADE450600AD6FF3 /* ArticleCategoryCoordinator.swift */; }; C03FCD422B0DD11C00C4EA0D /* UITabbarItem+.swift in Sources */ = {isa = PBXBuildFile; fileRef = C03FCD412B0DD11C00C4EA0D /* UITabbarItem+.swift */; }; @@ -242,6 +242,9 @@ C09A33242A630A6400B40770 /* BookmarkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DF036C2A5A9C9A0037F740 /* BookmarkViewController.swift */; }; C09A564D2B030C070012A7FD /* GetPregnancyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C09A564C2B030C070012A7FD /* GetPregnancyViewModel.swift */; }; C09A564F2B030CB20012A7FD /* GetPregnancyViewModelImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C09A564E2B030CB20012A7FD /* GetPregnancyViewModelImpl.swift */; }; + C0A4BBE82B19F6EB000BDDC5 /* ChallengeViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A4BBE72B19F6EB000BDDC5 /* ChallengeViewControllerTests.swift */; }; + C0A4BBEA2B19F709000BDDC5 /* ChallengeViewModelSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A4BBE92B19F709000BDDC5 /* ChallengeViewModelSpy.swift */; }; + C0AC2ACF2B199474003BDFC0 /* ChallengeFailure_Client.json in Resources */ = {isa = PBXBuildFile; fileRef = C0AC2ACE2B199474003BDFC0 /* ChallengeFailure_Client.json */; }; C0B15E132AC010CD0058D56B /* LHKingfisherService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B15E122AC010CD0058D56B /* LHKingfisherService.swift */; }; C0B15E152AC011840058D56B /* LHLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B15E142AC011840058D56B /* LHLabel.swift */; }; C0B15E172AC015360058D56B /* LHImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B15E162AC015360058D56B /* LHImageView.swift */; }; @@ -254,6 +257,10 @@ C0B15E272AC104D50058D56B /* LHImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B15E262AC104D50058D56B /* LHImageButton.swift */; }; C0B15E292AC106CA0058D56B /* CurriculumListByWeekTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B15E282AC106CA0058D56B /* CurriculumListByWeekTableView.swift */; }; C0B15E2B2AC115F90058D56B /* LHView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B15E2A2AC115F90058D56B /* LHView.swift */; }; + C0B234FE2B19995F00AFC5BB /* ChallengeViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B234FD2B19995F00AFC5BB /* ChallengeViewModelTests.swift */; }; + C0B235002B199E4000AFC5BB /* ChallengeManagerStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B234FF2B199E4000AFC5BB /* ChallengeManagerStub.swift */; }; + C0B235022B199F4D00AFC5BB /* ChallengeNavigationDummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B235012B199F4D00AFC5BB /* ChallengeNavigationDummy.swift */; }; + C0B235042B19AA4800AFC5BB /* ChallengeViewModelTestSetUp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B235032B19AA4800AFC5BB /* ChallengeViewModelTestSetUp.swift */; }; C0D47B602B08B640003B66E6 /* ChallengeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D47B5F2B08B640003B66E6 /* ChallengeViewModel.swift */; }; C0D47B622B08B6A3003B66E6 /* ChallengeViewModelImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D47B612B08B6A3003B66E6 /* ChallengeViewModelImpl.swift */; }; C0D47B642B09A7A7003B66E6 /* TodayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D47B632B09A7A7003B66E6 /* TodayViewModel.swift */; }; @@ -322,8 +329,9 @@ C0F029E62A5FB9E000E0D185 /* ContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0F029E52A5FB9DF00E0D185 /* ContainerView.swift */; }; C0F029E82A5FB9EF00E0D185 /* RoundContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0F029E72A5FB9EF00E0D185 /* RoundContainerView.swift */; }; C0F029EA2A5FD32900E0D185 /* LHRoundButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0F029E92A5FD32900E0D185 /* LHRoundButton.swift */; }; - C0F62FCA2A67CDCE0003ADFA /* BookmarkDetailCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A8980C12A5FD6AF00746C58 /* BookmarkDetailCollectionViewCell.swift */; }; C0F62FE72A691FC40003ADFA /* LHLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0F62FE62A691FC40003ADFA /* LHLoadingView.swift */; }; + C0FF46BC2B1963D300D487D7 /* ChallengeSuccess.json in Resources */ = {isa = PBXBuildFile; fileRef = C0FF46BB2B1963D300D487D7 /* ChallengeSuccess.json */; }; + C0FF46BE2B19786500D487D7 /* ChallengeFailure_Server.json in Resources */ = {isa = PBXBuildFile; fileRef = C0FF46BD2B1974CC00D487D7 /* ChallengeFailure_Server.json */; }; D34280772A66B90C00DA1499 /* UILabelPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = D34280762A66B90C00DA1499 /* UILabelPadding.swift */; }; D342807A2A67F12200DA1499 /* ChallengeDataResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D34280792A67F12200DA1499 /* ChallengeDataResponse.swift */; }; D35272D12A681E13002D7FCB /* ChallengeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D35272D02A681E13002D7FCB /* ChallengeData.swift */; }; @@ -344,7 +352,6 @@ F47329E12A66F5D0001605D4 /* CurriculumResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = F47329E02A66F5D0001605D4 /* CurriculumResponse.swift */; }; F4DB30B02A611C9700413EB9 /* CurriculumWeekData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4DB30AF2A611C9700413EB9 /* CurriculumWeekData.swift */; }; F4DB30B82A612D2800413EB9 /* CurriculumArticleByWeekTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4DB30B72A612D2800413EB9 /* CurriculumArticleByWeekTableViewCell.swift */; }; - F4DB30BC2A61691F00413EB9 /* CurriculumArticleByWeekHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4DB30BB2A61691F00413EB9 /* CurriculumArticleByWeekHeaderView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -431,7 +438,6 @@ B532E83D2A5525C700F0DB19 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; B532E83F2A5525C700F0DB19 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B532E8442A5525C800F0DB19 /* LionHeart-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "LionHeart-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - B532E8482A5525C800F0DB19 /* LionHeart_iOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LionHeart_iOSTests.swift; sourceTree = ""; }; B532E84E2A5525C800F0DB19 /* LionHeart-iOSUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "LionHeart-iOSUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; B532E8522A5525C800F0DB19 /* LionHeart_iOSUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LionHeart_iOSUITests.swift; sourceTree = ""; }; B532E8542A5525C800F0DB19 /* LionHeart_iOSUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LionHeart_iOSUITestsLaunchTests.swift; sourceTree = ""; }; @@ -479,7 +485,7 @@ B59BFD3E2ADBBF2B005D2D81 /* CurriculumFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurriculumFactory.swift; sourceTree = ""; }; B59BFD402ADBBFB2005D2D81 /* MyPageFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageFactory.swift; sourceTree = ""; }; B59BFD422ADBBFF5005D2D81 /* ArticleFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleFactory.swift; sourceTree = ""; }; - B5BE51C02B15B8F100042EF3 /* ServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceTests.swift; sourceTree = ""; }; + B5BE51C02B15B8F100042EF3 /* ChallengeServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeServiceTests.swift; sourceTree = ""; }; B5BE51C62B15C75F00042EF3 /* URLSessionStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionStub.swift; sourceTree = ""; }; B5BE51C82B15CB9600042EF3 /* JSONLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONLoader.swift; sourceTree = ""; }; B5C6A2B12A5DB0B10021BE5E /* ArticleDetailTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleDetailTableView.swift; sourceTree = ""; }; @@ -556,6 +562,7 @@ C03FCD412B0DD11C00C4EA0D /* UITabbarItem+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITabbarItem+.swift"; sourceTree = ""; }; C04BE3AC2ADD4F5D001967B5 /* TodayCoordinatorImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayCoordinatorImpl.swift; sourceTree = ""; }; C04BE3AE2ADD4F7F001967B5 /* TodayAdaptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayAdaptor.swift; sourceTree = ""; }; + C060EAF42B195C6600AB2855 /* LionHeart-iOS.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LionHeart-iOS.xctestplan"; sourceTree = ""; }; C06318072B10623000DE1995 /* ChallengeViewDiffableModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeViewDiffableModel.swift; sourceTree = ""; }; C06318132B1064C400DE1995 /* ArticleDataByWeek.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleDataByWeek.swift; sourceTree = ""; }; C06318192B10658400DE1995 /* ArticleDetailSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleDetailSection.swift; sourceTree = ""; }; @@ -599,6 +606,9 @@ C09A33212A62D46300B40770 /* LHToastView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LHToastView.swift; sourceTree = ""; }; C09A564C2B030C070012A7FD /* GetPregnancyViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPregnancyViewModel.swift; sourceTree = ""; }; C09A564E2B030CB20012A7FD /* GetPregnancyViewModelImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPregnancyViewModelImpl.swift; sourceTree = ""; }; + C0A4BBE72B19F6EB000BDDC5 /* ChallengeViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeViewControllerTests.swift; sourceTree = ""; }; + C0A4BBE92B19F709000BDDC5 /* ChallengeViewModelSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeViewModelSpy.swift; sourceTree = ""; }; + C0AC2ACE2B199474003BDFC0 /* ChallengeFailure_Client.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = ChallengeFailure_Client.json; sourceTree = ""; }; C0B15E122AC010CD0058D56B /* LHKingfisherService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LHKingfisherService.swift; sourceTree = ""; }; C0B15E142AC011840058D56B /* LHLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LHLabel.swift; sourceTree = ""; }; C0B15E162AC015360058D56B /* LHImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LHImageView.swift; sourceTree = ""; }; @@ -611,6 +621,10 @@ C0B15E262AC104D50058D56B /* LHImageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LHImageButton.swift; sourceTree = ""; }; C0B15E282AC106CA0058D56B /* CurriculumListByWeekTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurriculumListByWeekTableView.swift; sourceTree = ""; }; C0B15E2A2AC115F90058D56B /* LHView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LHView.swift; sourceTree = ""; }; + C0B234FD2B19995F00AFC5BB /* ChallengeViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeViewModelTests.swift; sourceTree = ""; }; + C0B234FF2B199E4000AFC5BB /* ChallengeManagerStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeManagerStub.swift; sourceTree = ""; }; + C0B235012B199F4D00AFC5BB /* ChallengeNavigationDummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeNavigationDummy.swift; sourceTree = ""; }; + C0B235032B19AA4800AFC5BB /* ChallengeViewModelTestSetUp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeViewModelTestSetUp.swift; sourceTree = ""; }; C0D47B5F2B08B640003B66E6 /* ChallengeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeViewModel.swift; sourceTree = ""; }; C0D47B612B08B6A3003B66E6 /* ChallengeViewModelImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeViewModelImpl.swift; sourceTree = ""; }; C0D47B632B09A7A7003B66E6 /* TodayViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewModel.swift; sourceTree = ""; }; @@ -685,6 +699,8 @@ C0F62FBE2A67CDB60003ADFA /* progressbar_5m.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = progressbar_5m.json; sourceTree = ""; }; C0F62FBF2A67CDB60003ADFA /* progressbar_6m.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = progressbar_6m.json; sourceTree = ""; }; C0F62FE62A691FC40003ADFA /* LHLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LHLoadingView.swift; sourceTree = ""; }; + C0FF46BB2B1963D300D487D7 /* ChallengeSuccess.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = ChallengeSuccess.json; sourceTree = ""; }; + C0FF46BD2B1974CC00D487D7 /* ChallengeFailure_Server.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = ChallengeFailure_Server.json; sourceTree = ""; }; D34280762A66B90C00DA1499 /* UILabelPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UILabelPadding.swift; sourceTree = ""; }; D34280792A67F12200DA1499 /* ChallengeDataResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeDataResponse.swift; sourceTree = ""; }; D35272D02A681E13002D7FCB /* ChallengeData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChallengeData.swift; sourceTree = ""; }; @@ -890,11 +906,13 @@ B532E8252A5525C600F0DB19 = { isa = PBXGroup; children = ( + C060EAF42B195C6600AB2855 /* LionHeart-iOS.xctestplan */, B532E8302A5525C600F0DB19 /* LionHeart-iOS */, B532E8472A5525C800F0DB19 /* LionHeart-iOSTests */, B532E8512A5525C800F0DB19 /* LionHeart-iOSUITests */, B532E82F2A5525C600F0DB19 /* Products */, C07CB8172A62C4D6000198CC /* Recovered References */, + C060EAF12B195A2800AB2855 /* Frameworks */, ); sourceTree = ""; }; @@ -927,9 +945,7 @@ isa = PBXGroup; children = ( B5BE51C32B15BD6200042EF3 /* URLSessionStub */, - B5BE51BB2B15B7B100042EF3 /* Today */, B5BE51BA2B15B79C00042EF3 /* Challenge */, - B532E8482A5525C800F0DB19 /* LionHeart_iOSTests.swift */, ); path = "LionHeart-iOSTests"; sourceTree = ""; @@ -1440,47 +1456,23 @@ B5BE51BA2B15B79C00042EF3 /* Challenge */ = { isa = PBXGroup; children = ( - B5BE51C52B15BDBD00042EF3 /* JSON */, - B5BE51BC2B15B80F00042EF3 /* ViewModelStub */, + C0A4BBE62B19F6D6000BDDC5 /* ViewControllerTests */, + C0A4BBE52B19F6CD000BDDC5 /* Stubs */, + C0A4BBE42B19F6BC000BDDC5 /* ViewModelTests */, + C0FF46BA2B1963BC00D487D7 /* ChallengeJSON */, B5BE51BF2B15B8C300042EF3 /* ServiceTests */, - B5BE51C22B15B8F600042EF3 /* ViewControllerTests */, ); path = Challenge; sourceTree = ""; }; - B5BE51BB2B15B7B100042EF3 /* Today */ = { - isa = PBXGroup; - children = ( - B5BE51CD2B15CC5300042EF3 /* JSON */, - B5BE51CF2B15CC5B00042EF3 /* ServiceTests */, - B5BE51CE2B15CC5700042EF3 /* ViewModelStub */, - B5BE51D02B15CC5F00042EF3 /* ViewControllerTests */, - ); - path = Today; - sourceTree = ""; - }; - B5BE51BC2B15B80F00042EF3 /* ViewModelStub */ = { - isa = PBXGroup; - children = ( - ); - path = ViewModelStub; - sourceTree = ""; - }; B5BE51BF2B15B8C300042EF3 /* ServiceTests */ = { isa = PBXGroup; children = ( - B5BE51C02B15B8F100042EF3 /* ServiceTests.swift */, + B5BE51C02B15B8F100042EF3 /* ChallengeServiceTests.swift */, ); path = ServiceTests; sourceTree = ""; }; - B5BE51C22B15B8F600042EF3 /* ViewControllerTests */ = { - isa = PBXGroup; - children = ( - ); - path = ViewControllerTests; - sourceTree = ""; - }; B5BE51C32B15BD6200042EF3 /* URLSessionStub */ = { isa = PBXGroup; children = ( @@ -1490,41 +1482,6 @@ path = URLSessionStub; sourceTree = ""; }; - B5BE51C52B15BDBD00042EF3 /* JSON */ = { - isa = PBXGroup; - children = ( - ); - path = JSON; - sourceTree = ""; - }; - B5BE51CD2B15CC5300042EF3 /* JSON */ = { - isa = PBXGroup; - children = ( - ); - path = JSON; - sourceTree = ""; - }; - B5BE51CE2B15CC5700042EF3 /* ViewModelStub */ = { - isa = PBXGroup; - children = ( - ); - path = ViewModelStub; - sourceTree = ""; - }; - B5BE51CF2B15CC5B00042EF3 /* ServiceTests */ = { - isa = PBXGroup; - children = ( - ); - path = ServiceTests; - sourceTree = ""; - }; - B5BE51D02B15CC5F00042EF3 /* ViewControllerTests */ = { - isa = PBXGroup; - children = ( - ); - path = ViewControllerTests; - sourceTree = ""; - }; B5C6A2C32A5EF4AC0021BE5E /* DTO */ = { isa = PBXGroup; children = ( @@ -1704,6 +1661,13 @@ path = CoordinatorImpl; sourceTree = ""; }; + C060EAF12B195A2800AB2855 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; C06318052B1061D600DE1995 /* ViewModel */ = { isa = PBXGroup; children = ( @@ -2004,6 +1968,33 @@ path = ViewModels; sourceTree = ""; }; + C0A4BBE42B19F6BC000BDDC5 /* ViewModelTests */ = { + isa = PBXGroup; + children = ( + C0B234FD2B19995F00AFC5BB /* ChallengeViewModelTests.swift */, + C0B235032B19AA4800AFC5BB /* ChallengeViewModelTestSetUp.swift */, + ); + path = ViewModelTests; + sourceTree = ""; + }; + C0A4BBE52B19F6CD000BDDC5 /* Stubs */ = { + isa = PBXGroup; + children = ( + C0B234FF2B199E4000AFC5BB /* ChallengeManagerStub.swift */, + C0B235012B199F4D00AFC5BB /* ChallengeNavigationDummy.swift */, + C0A4BBE92B19F709000BDDC5 /* ChallengeViewModelSpy.swift */, + ); + path = Stubs; + sourceTree = ""; + }; + C0A4BBE62B19F6D6000BDDC5 /* ViewControllerTests */ = { + isa = PBXGroup; + children = ( + C0A4BBE72B19F6EB000BDDC5 /* ChallengeViewControllerTests.swift */, + ); + path = ViewControllerTests; + sourceTree = ""; + }; C0DF03292A5A91450037F740 /* Protocols */ = { isa = PBXGroup; children = ( @@ -2061,6 +2052,16 @@ path = OnboardingComponent; sourceTree = ""; }; + C0FF46BA2B1963BC00D487D7 /* ChallengeJSON */ = { + isa = PBXGroup; + children = ( + C0FF46BB2B1963D300D487D7 /* ChallengeSuccess.json */, + C0FF46BD2B1974CC00D487D7 /* ChallengeFailure_Server.json */, + C0AC2ACE2B199474003BDFC0 /* ChallengeFailure_Client.json */, + ); + path = ChallengeJSON; + sourceTree = ""; + }; D30CBA182A60383800C8636B /* Model */ = { isa = PBXGroup; children = ( @@ -2164,6 +2165,8 @@ B532E8462A5525C800F0DB19 /* PBXTargetDependency */, ); name = "LionHeart-iOSTests"; + packageProductDependencies = ( + ); productName = "LionHeart-iOSTests"; productReference = B532E8442A5525C800F0DB19 /* LionHeart-iOSTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; @@ -2270,6 +2273,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + C0AC2ACF2B199474003BDFC0 /* ChallengeFailure_Client.json in Resources */, + C0FF46BE2B19786500D487D7 /* ChallengeFailure_Server.json in Resources */, + C0FF46BC2B1963D300D487D7 /* ChallengeSuccess.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2359,8 +2365,6 @@ C0D47B942B0B2C46003B66E6 /* MyPageViewModelImpl.swift in Sources */, C0D47B622B08B6A3003B66E6 /* ChallengeViewModelImpl.swift in Sources */, C0DF039F2A5CABC10037F740 /* GetPregnancyViewController.swift in Sources */, - F4DB30BC2A61691F00413EB9 /* CurriculumArticleByWeekHeaderView.swift in Sources */, - F4DB30BC2A61691F00413EB9 /* CurriculumArticleByWeekHeaderView.swift in Sources */, C0DF039A2A5B908E0037F740 /* CGColor+.swift in Sources */, C0D47B662B09A845003B66E6 /* TodayViewModelImpl.swift in Sources */, C034EE1E2ADE44E200AD6FF3 /* ArticleCategoryAdaptor.swift in Sources */, @@ -2384,6 +2388,7 @@ C06E38212A65351600B00600 /* SignUpRequest.swift in Sources */, C003CC432ADA4EFF00AFFAAC /* MyPageNavigation.swift in Sources */, C003CC512ADA4FA600AFFAAC /* CompleteOnboardingNavigation.swift in Sources */, + B59FC6BD2B19C569000996CA /* CurriculumArticleByWeekHeaderView.swift in Sources */, C09A33232A62D46300B40770 /* LHToastView.swift in Sources */, C0D47B962B0B3739003B66E6 /* MyPageModel.swift in Sources */, B53F4EF82ADE2FB0001C5752 /* ChallengeCoordinatorImpl.swift in Sources */, @@ -2530,7 +2535,6 @@ C0DF037B2A5A9CF30037F740 /* OnboardingViewController.swift in Sources */, 4A3F0C312B05E8E8004BD076 /* CompleteOnboardingViewModelImpl.swift in Sources */, C09217682A605DEE00231C66 /* OnboardingFetalNicknameTextFieldResultType.swift in Sources */, - C0F62FCA2A67CDCE0003ADFA /* BookmarkDetailCollectionViewCell.swift in Sources */, C0DF03592A5A9BF80037F740 /* ArticleCategoryViewController.swift in Sources */, B5234FB32B0B451800D6EE58 /* ArticleListByCategoryDiffableModel.swift in Sources */, C00780BA2A60149D0043EB36 /* LHTodayArticleTitle.swift in Sources */, @@ -2588,9 +2592,14 @@ buildActionMask = 2147483647; files = ( B5BE51C92B15CB9600042EF3 /* JSONLoader.swift in Sources */, - C034EDE02ADE3A4A00AD6FF3 /* LionHeart_iOSTests.swift in Sources */, - B5BE51C12B15B8F100042EF3 /* ServiceTests.swift in Sources */, + C0B235042B19AA4800AFC5BB /* ChallengeViewModelTestSetUp.swift in Sources */, + C0A4BBE82B19F6EB000BDDC5 /* ChallengeViewControllerTests.swift in Sources */, + B5BE51C12B15B8F100042EF3 /* ChallengeServiceTests.swift in Sources */, + C0B234FE2B19995F00AFC5BB /* ChallengeViewModelTests.swift in Sources */, + C0B235022B199F4D00AFC5BB /* ChallengeNavigationDummy.swift in Sources */, + C0A4BBEA2B19F709000BDDC5 /* ChallengeViewModelSpy.swift in Sources */, B5BE51C72B15C75F00042EF3 /* URLSessionStub.swift in Sources */, + C0B235002B199E4000AFC5BB /* ChallengeManagerStub.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2834,7 +2843,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LionHeart-iOS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/LionHeart-iOS"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LionHeart-iOS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)LionHeart-iOS"; }; name = Debug; }; @@ -2860,7 +2869,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LionHeart-iOS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/LionHeart-iOS"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LionHeart-iOS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)LionHeart-iOS"; }; name = Release; }; diff --git a/LionHeart-iOS/LionHeart-iOS.xcodeproj/xcshareddata/xcschemes/LionHeart-iOS.xcscheme b/LionHeart-iOS/LionHeart-iOS.xcodeproj/xcshareddata/xcschemes/LionHeart-iOS.xcscheme index dbf061e0..137bdf7d 100644 --- a/LionHeart-iOS/LionHeart-iOS.xcodeproj/xcshareddata/xcschemes/LionHeart-iOS.xcscheme +++ b/LionHeart-iOS/LionHeart-iOS.xcodeproj/xcshareddata/xcschemes/LionHeart-iOS.xcscheme @@ -26,8 +26,13 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - shouldAutocreateTestPlan = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> + + + + ! init(viewModel: some ChallengeViewModel) { @@ -80,7 +80,7 @@ final class ChallengeViewController: UIViewController, ChallengeViewControllerab } } -private extension ChallengeViewController { +extension ChallengeViewController { func configureData(_ input: ChallengeData) { setText(by: input) setImage(by: input) diff --git a/LionHeart-iOS/LionHeart-iOS/Scenes/Challenge/ViewModel/ChallengeViewModelImpl.swift b/LionHeart-iOS/LionHeart-iOS/Scenes/Challenge/ViewModel/ChallengeViewModelImpl.swift index 57fdae20..6f5aa3c1 100644 --- a/LionHeart-iOS/LionHeart-iOS/Scenes/Challenge/ViewModel/ChallengeViewModelImpl.swift +++ b/LionHeart-iOS/LionHeart-iOS/Scenes/Challenge/ViewModel/ChallengeViewModelImpl.swift @@ -10,11 +10,11 @@ import Combine final class ChallengeViewModelImpl: ChallengeViewModel, ChallengeViewModelPresentable { - private enum FlowType { case bookmarkButtonTapped, myPageButtonTapped } + enum FlowType { case bookmarkButtonTapped, myPageButtonTapped } - private let navigationSubject = PassthroughSubject() + var navigationSubject = PassthroughSubject() private var cancelBag = Set() - private let errorSubject = PassthroughSubject() + let errorSubject = PassthroughSubject() private var navigator: ChallengeNavigation private var manager: ChallengeManager @@ -44,11 +44,15 @@ final class ChallengeViewModelImpl: ChallengeViewModel, ChallengeViewModelPresen .store(in: &cancelBag) input.navigationLeftButtonTapped - .sink { [weak self] in self?.navigationSubject.send(.bookmarkButtonTapped) } + .sink { [weak self] in + self?.navigationSubject.send(.bookmarkButtonTapped) + } .store(in: &cancelBag) input.navigationRightButtonTapped - .sink { [weak self] in self?.navigationSubject.send(.myPageButtonTapped) } + .sink { + [weak self] in self?.navigationSubject.send(.myPageButtonTapped) + } .store(in: &cancelBag) let viewWillAppearSubject = input.viewWillAppearSubject diff --git a/LionHeart-iOS/LionHeart-iOS/Scenes/CurriculmumListWeek/ViewController/CurriculumListWeekViewController.swift b/LionHeart-iOS/LionHeart-iOS/Scenes/CurriculmumListWeek/ViewController/CurriculumListWeekViewController.swift index 197f65b8..c898607f 100644 --- a/LionHeart-iOS/LionHeart-iOS/Scenes/CurriculmumListWeek/ViewController/CurriculumListWeekViewController.swift +++ b/LionHeart-iOS/LionHeart-iOS/Scenes/CurriculmumListWeek/ViewController/CurriculumListWeekViewController.swift @@ -22,7 +22,9 @@ final class CurriculumListWeekViewController: UIViewController, CurriculumArticl private lazy var navigationBar = LHNavigationBarView(type: .curriculumByWeek, viewController: self) private var datasoruce: UITableViewDiffableDataSource! - private lazy var headerView = CurriculumArticleByWeekHeaderView(frame: .init(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.width*(200 / 375))) + private lazy var headerView = + CurriculumArticleByWeekHeaderView(frame: .init(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.width*(200 / 375))) + private let curriculumListByWeekTableView = CurriculumListByWeekTableView() private let viewModel: any CurriculumListWeekViewModel diff --git a/LionHeart-iOS/LionHeart-iOSTests/Challenge/ChallengeJSON/ChallengeFailure_Client.json b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ChallengeJSON/ChallengeFailure_Client.json new file mode 100644 index 00000000..0a0b1e46 --- /dev/null +++ b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ChallengeJSON/ChallengeFailure_Client.json @@ -0,0 +1,5 @@ +{ + "code": "N004", + "message": "클라이언트에러" +} + diff --git a/LionHeart-iOS/LionHeart-iOSTests/Challenge/ChallengeJSON/ChallengeFailure_Server.json b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ChallengeJSON/ChallengeFailure_Server.json new file mode 100644 index 00000000..9107992a --- /dev/null +++ b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ChallengeJSON/ChallengeFailure_Server.json @@ -0,0 +1,4 @@ +{ + "code": "I001", + "message": "서버에러가발생했습니다" +} diff --git a/LionHeart-iOS/LionHeart-iOSTests/Challenge/ChallengeJSON/ChallengeSuccess.json b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ChallengeJSON/ChallengeSuccess.json new file mode 100644 index 00000000..90dd14ae --- /dev/null +++ b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ChallengeJSON/ChallengeSuccess.json @@ -0,0 +1,14 @@ +{ + "code": "SUCCESS", + "message": "정보조회에성공했습니다", + "data": { + "babyNickname": "Test닉네임", + "day": 10, + "level": "LEVEL_ONE", + "attendances": [ + "11/1", + "11/2", + "11/3" + ] + } +} diff --git a/LionHeart-iOS/LionHeart-iOSTests/Challenge/ServiceTests/ChallengeServiceTests.swift b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ServiceTests/ChallengeServiceTests.swift new file mode 100644 index 00000000..664c7790 --- /dev/null +++ b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ServiceTests/ChallengeServiceTests.swift @@ -0,0 +1,90 @@ +// +// ChallengeServiceTests.swift +// LionHeart-iOSTests +// +// Created by 김의성 on 12/01/23. +// + +import XCTest +@testable import LionHeart_iOS + +final class ChallengeServiceTests: XCTestCase { + + var apiService: Requestable! + var urlSession: URLSessionStub! + var jsonLoader: JSONLoader! + var url: URL! + + override func setUpWithError() throws { + self.jsonLoader = JSONLoader() + } + + override func tearDownWithError() throws { + self.jsonLoader = nil + self.url = nil + } + + func test_챌린지API_호출을_성공했을때() async throws { + //given + let urlRequest = try self.setChallengeAPITest(fileName: "ChallengeSuccess") + + //when + let returnData: ChallengeDataResponse? = try await self.apiService.request(urlRequest) + let result = try XCTUnwrap(returnData, "언래핑실패") + + //then + let expectation = ChallengeDataResponse(babyNickname: "Test닉네임", day: 10, level: "LEVEL_ONE", attendances: ["11/1","11/2","11/3"]) + XCTAssertEqual(result.babyNickname, expectation.babyNickname) + XCTAssertEqual(result.day, expectation.day) + XCTAssertEqual(result.attendances, expectation.attendances) + } + + func test_챌린지API_호출했을때_서버에러가발생한경우() async throws { + //given + let urlRequest = try self.setChallengeAPITest(fileName: "ChallengeFailure_Server") + + //when + var willOccureError: NetworkError? + do { + let _: ChallengeDataResponse? = try await self.apiService.request(urlRequest) + XCTFail("성공할수없는 case입니다") + } catch { + let error = error as? NetworkError + willOccureError = error + } + + //then + let expectation = NetworkError.serverError + XCTAssertEqual(willOccureError, expectation) + } + + func test_챌린지API_호출했을때_존재하지않는_챌린지인경우() async throws { + //given + let urlRequest = try self.setChallengeAPITest(fileName: "ChallengeFailure_Client") + + //when + var willOccureError: NetworkError? + do { + let _: ChallengeDataResponse? = try await self.apiService.request(urlRequest) + XCTFail("성공할수없는 case입니다") + } catch { + let error = error as? NetworkError + willOccureError = error + } + + //then + let expectation = NetworkError.clientError(code: "N004", message: "클라이언트에러") + XCTAssertEqual(willOccureError, expectation) + } +} + +private extension ChallengeServiceTests { + func setChallengeAPITest(fileName: String) throws -> URLRequest { + self.url = jsonLoader.load(fileName: fileName) + let data = try Data(contentsOf: self.url) + let urlRequest = URLRequest(url: self.url) + self.urlSession = URLSessionStub(data: data) + self.apiService = APIService(session: urlSession) + return urlRequest + } +} diff --git a/LionHeart-iOS/LionHeart-iOSTests/Challenge/ServiceTests/ServiceTests.swift b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ServiceTests/ServiceTests.swift deleted file mode 100644 index 67b46eef..00000000 --- a/LionHeart-iOS/LionHeart-iOSTests/Challenge/ServiceTests/ServiceTests.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// ServiceTests.swift -// LionHeart-iOSTests -// -// Created by 김민재 on 11/28/23. -// - -import XCTest - -final class ServiceTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - } - - func testPerformanceExample() throws { - // 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/LionHeart-iOS/LionHeart-iOSTests/Challenge/Stubs/ChallengeManagerStub.swift b/LionHeart-iOS/LionHeart-iOSTests/Challenge/Stubs/ChallengeManagerStub.swift new file mode 100644 index 00000000..319d21e7 --- /dev/null +++ b/LionHeart-iOS/LionHeart-iOSTests/Challenge/Stubs/ChallengeManagerStub.swift @@ -0,0 +1,18 @@ +// +// ChallengeManagerStub.swift +// LionHeart-iOSTests +// +// Created by uiskim on 2023/12/01. +// + +import Foundation +@testable import LionHeart_iOS + +final class ChallengeManagerStub: ChallengeManager { + var returnValue: ChallengeDataResponse? + + func inquireChallengeInfo() async throws -> LionHeart_iOS.ChallengeData { + guard let returnValue else { throw NetworkError.badCasting } + return returnValue.toAppData() + } +} diff --git a/LionHeart-iOS/LionHeart-iOSTests/Challenge/Stubs/ChallengeNavigationDummy.swift b/LionHeart-iOS/LionHeart-iOSTests/Challenge/Stubs/ChallengeNavigationDummy.swift new file mode 100644 index 00000000..e37d2250 --- /dev/null +++ b/LionHeart-iOS/LionHeart-iOSTests/Challenge/Stubs/ChallengeNavigationDummy.swift @@ -0,0 +1,24 @@ +// +// ChallengeNavigationStub.swift +// LionHeart-iOSTests +// +// Created by uiskim on 2023/12/01. +// + +import Foundation +@testable import LionHeart_iOS + +final class ChallengeNavigationDummy: ChallengeNavigation { + + func navigationRightButtonTapped() { + print("왼쪽버튼눌림") + } + + func navigationLeftButtonTapped() { + print("오른쪽버튼눌림") + } + + func checkTokenIsExpired() { + print("앱강제종료") + } +} diff --git a/LionHeart-iOS/LionHeart-iOSTests/Challenge/Stubs/ChallengeViewModelSpy.swift b/LionHeart-iOS/LionHeart-iOSTests/Challenge/Stubs/ChallengeViewModelSpy.swift new file mode 100644 index 00000000..3b4a3aa5 --- /dev/null +++ b/LionHeart-iOS/LionHeart-iOSTests/Challenge/Stubs/ChallengeViewModelSpy.swift @@ -0,0 +1,47 @@ +// +// ChallengeViewModelStub.swift +// LionHeart-iOSTests +// +// Created by uiskim on 2023/12/01. +// + +import Foundation +import Combine +@testable import LionHeart_iOS + +final class ChallengeViewModelSpy: ChallengeViewModel { + + enum FlowType { case bookmarkButtonTapped, myPageButtonTapped } + + var navigationSubject = PassthroughSubject() + private var cancelBag = Set() + let errorSubject = PassthroughSubject() + var willPublishedData: ChallengeData? + + var inputData: ChallengeData! + + func transform(input: ChallengeViewModelInput) -> ChallengeViewModelOutput { + + input.navigationLeftButtonTapped + .sink { [weak self] in + self?.navigationSubject.send(.bookmarkButtonTapped) + } + .store(in: &cancelBag) + + input.navigationRightButtonTapped + .sink { + [weak self] in self?.navigationSubject.send(.myPageButtonTapped) + } + .store(in: &cancelBag) + + let viewWillAppearSubject = input.viewWillAppearSubject + .flatMap { _ -> AnyPublisher in + self.willPublishedData = self.inputData + return Just(self.inputData) + .eraseToAnyPublisher() + } + .eraseToAnyPublisher() + + return ChallengeViewModelOutput(viewWillAppearSubject: viewWillAppearSubject) + } +} diff --git a/LionHeart-iOS/LionHeart-iOSTests/Challenge/ViewControllerTests/ChallengeViewControllerTests.swift b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ViewControllerTests/ChallengeViewControllerTests.swift new file mode 100644 index 00000000..0645bf2c --- /dev/null +++ b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ViewControllerTests/ChallengeViewControllerTests.swift @@ -0,0 +1,131 @@ +// +// ChallengeViewControllerTests.swift +// LionHeart-iOSTests +// +// Created by uiskim on 2023/12/01. +// + +import XCTest +import Combine +@testable import LionHeart_iOS + +final class ChallengeViewControllerTests: XCTestCase { + var viewModel: ChallengeViewModelSpy! + var cancelBag: Set! + + override func setUpWithError() throws { + self.viewModel = ChallengeViewModelSpy() + self.cancelBag = Set() + } + + override func tearDownWithError() throws { + self.viewModel = nil + self.cancelBag = nil + } + + func test_viewWillAppear의_시점이_viewModel에_잘_전달되었는지() { + //given + let inputData = ChallengeData(babyDaddyName: "튼튼이", howLongDay: 10, daddyLevel: "LEVEL_ONE", daddyAttendances: ["11/1", "11/2", "11/3"]) + self.viewModel.inputData = inputData + let viewController = ChallengeViewController(viewModel: self.viewModel) + + //when + viewController.loadViewIfNeeded() + viewController.viewWillAppear(false) + + XCTAssertEqual(self.viewModel.willPublishedData, inputData) + } + + func test_ChallengeVC의_UserData가_UI에_잘_반영되었는지() throws { + //given + let inputData = ChallengeData(babyDaddyName: "튼튼이", howLongDay: 10, daddyLevel: "LEVEL_ONE", daddyAttendances: ["11/1", "11/2", "11/3"]) + let viewController = ChallengeViewController(viewModel: self.viewModel) + + //when + viewController.loadViewIfNeeded() + viewController.configureData(inputData) + + //then + XCTAssertEqual(viewController.nicknameLabel.text, "튼튼이아빠 님,") + XCTAssertEqual(viewController.challengeDayLabel.text, "10일째 도전 중") + XCTAssertEqual(viewController.challengelevelLabel.text, "사자력 Lv.1") + + } + + func test_ChallengeVC의_CollectionView에_데이터가_잘_반영되었는지() { + //given + let inputData = ChallengeData(babyDaddyName: "튼튼이", howLongDay: 10, daddyLevel: "LEVEL_ONE", daddyAttendances: ["11/1", "11/2", "11/3"]) + let viewController = ChallengeViewController(viewModel: self.viewModel) + + //when + viewController.loadViewIfNeeded() + viewController.configureData(inputData) + + //then + let numberOfRows = viewController.challengeDayCheckCollectionView.numberOfItems(inSection: 0) + XCTAssertEqual(numberOfRows, 20) + + let cell0 = viewController.challengeDayCheckCollectionView.dataSource?.collectionView(viewController.challengeDayCheckCollectionView, cellForItemAt: IndexPath(row: 0, section: 0)) as! ChallengeDayCheckCollectionViewCollectionViewCell + XCTAssertEqual(cell0.countLabel.text, inputData.daddyAttendances[0]) + XCTAssertEqual(cell0.backgroundColor, .designSystem(.background)) + XCTAssertEqual(cell0.countLabel.textColor, .designSystem(.white)) + + let cell1 = viewController.challengeDayCheckCollectionView.dataSource?.collectionView(viewController.challengeDayCheckCollectionView, cellForItemAt: IndexPath(row: 1, section: 0)) as! ChallengeDayCheckCollectionViewCollectionViewCell + XCTAssertEqual(cell1.countLabel.text, inputData.daddyAttendances[1]) + XCTAssertEqual(cell1.backgroundColor, .designSystem(.background)) + XCTAssertEqual(cell1.countLabel.textColor, .designSystem(.white)) + + let cell2 = viewController.challengeDayCheckCollectionView.dataSource?.collectionView(viewController.challengeDayCheckCollectionView, cellForItemAt: IndexPath(row: 2, section: 0)) as! ChallengeDayCheckCollectionViewCollectionViewCell + XCTAssertEqual(cell2.countLabel.text, inputData.daddyAttendances[2]) + XCTAssertEqual(cell2.backgroundColor, .designSystem(.background)) + XCTAssertEqual(cell2.countLabel.textColor, .designSystem(.white)) + + let cell3 = viewController.challengeDayCheckCollectionView.dataSource?.collectionView(viewController.challengeDayCheckCollectionView, cellForItemAt: IndexPath(row: 3, section: 0)) as! ChallengeDayCheckCollectionViewCollectionViewCell + XCTAssertEqual(cell3.countLabel.text, "\(0+3+1)") + XCTAssertEqual(cell3.backgroundColor, .designSystem(.gray1000)) + } + + func test_Navigation의_북마크버튼이_잘_동작하는지() { + //given + let expectation = XCTestExpectation(description: "네비게이션왼쪽버튼이 눌렸을때") + let viewController = ChallengeViewController(viewModel: self.viewModel) + viewController.loadViewIfNeeded() + + //when + var navigationType: ChallengeViewModelSpy.FlowType? + viewModel.navigationSubject + .sink { type in + navigationType = type + expectation.fulfill() + } + .store(in: &cancelBag) + + viewController.navigationBar.rightFirstBarItem.sendActions(for: .touchUpInside) + + //then + wait(for: [expectation], timeout: 0.3) + XCTAssertEqual(navigationType, .bookmarkButtonTapped) + } + + func test_Navigation의_마이페이지버튼이_잘_동작하는지() { + //given + let expectation = XCTestExpectation(description: "네비게이션오른쪽버튼이 눌렸을때") + let viewController = ChallengeViewController(viewModel: self.viewModel) + viewController.loadViewIfNeeded() + + //when + var navigationType: ChallengeViewModelSpy.FlowType? + viewModel.navigationSubject + .sink { type in + navigationType = type + expectation.fulfill() + } + .store(in: &cancelBag) + + viewController.navigationBar.rightSecondBarItem.sendActions(for: .touchUpInside) + + //then + wait(for: [expectation], timeout: 0.3) + XCTAssertEqual(navigationType, .myPageButtonTapped) + } +} diff --git a/LionHeart-iOS/LionHeart-iOSTests/Challenge/ViewModelTests/ChallengeViewModelTestSetUp.swift b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ViewModelTests/ChallengeViewModelTestSetUp.swift new file mode 100644 index 00000000..a68a9f14 --- /dev/null +++ b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ViewModelTests/ChallengeViewModelTestSetUp.swift @@ -0,0 +1,52 @@ +// +// ChallengeViewModelTestSetUp.swift +// LionHeart-iOSTests +// +// Created by uiskim on 2023/12/01. +// + +import XCTest +import Combine + +@testable import LionHeart_iOS + +class ChallengeViewModelTestSetUp: XCTestCase { + + var navigationLeftButtonTapped: PassthroughSubject! + var navigationRightButtonTapped: PassthroughSubject! + var viewWillAppearSubject: PassthroughSubject! + + var input: ChallengeViewModelInput! + var output: ChallengeViewModelOutput! + + var manager: ChallengeManagerStub! + var navigation: ChallengeNavigationDummy! + var viewModel: ChallengeViewModelImpl! + var cancelBag: Set! + + override func setUp() { + self.manager = ChallengeManagerStub() + self.navigation = ChallengeNavigationDummy() + self.viewModel = ChallengeViewModelImpl(navigator: self.navigation, manager: self.manager) + + self.navigationLeftButtonTapped = PassthroughSubject() + self.navigationRightButtonTapped = PassthroughSubject() + self.viewWillAppearSubject = PassthroughSubject() + + self.input = .init(navigationLeftButtonTapped: self.navigationLeftButtonTapped, navigationRightButtonTapped: self.navigationRightButtonTapped, viewWillAppearSubject: self.viewWillAppearSubject) + + self.output = viewModel.transform(input: self.input) + self.cancelBag = Set() + } + + override func tearDown() { + self.navigationLeftButtonTapped = nil + self.navigationRightButtonTapped = nil + self.viewWillAppearSubject = nil + self.input = nil + self.output = nil + self.manager = nil + self.navigation = nil + self.viewModel = nil + } +} diff --git a/LionHeart-iOS/LionHeart-iOSTests/Challenge/ViewModelTests/ChallengeViewModelTests.swift b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ViewModelTests/ChallengeViewModelTests.swift new file mode 100644 index 00000000..4b6174cb --- /dev/null +++ b/LionHeart-iOS/LionHeart-iOSTests/Challenge/ViewModelTests/ChallengeViewModelTests.swift @@ -0,0 +1,103 @@ +// +// ChallengeViewModelTests.swift +// LionHeart-iOSTests +// +// Created by uiskim on 2023/12/01. +// + +import XCTest +import Combine + +@testable import LionHeart_iOS + +final class ChallengeViewModelTests: ChallengeViewModelTestSetUp { + + func test_ChallengeVM의_viewWillAppear이후의_AppData변환이_잘이루어졌을때() { + //given + let expectation = XCTestExpectation(description: "manager에서 제대로된 값이 들어왔을때") + self.manager.returnValue = .init(babyNickname: "test", day: 12, level: "LEVEL_ONE", attendances: []) + + //when + var data: ChallengeData? + output.viewWillAppearSubject + .sink { value in + data = value + expectation.fulfill() + } + .store(in: &cancelBag) + viewWillAppearSubject.send(()) + + //then + let expectedValue = ChallengeData(babyDaddyName: "test", howLongDay: 12, daddyLevel: "LEVEL_ONE", daddyAttendances: []) + wait(for: [expectation], timeout: 0.2) + XCTAssertEqual(data, expectedValue) + } + + func test_ChallengeVM의_viewWillAppear이후의_제대로된데이터가_전달되지않았을때() { + //given + let expectation = XCTestExpectation(description: "manager에서 nil이 들어왔을때") + self.manager.returnValue = nil + + //when + var data: ChallengeData? + var willOccureError: NetworkError? + + output.viewWillAppearSubject + .sink { value in + data = value + expectation.fulfill() + } + .store(in: &cancelBag) + + self.viewModel.errorSubject + .sink { willOccureError = $0 } + .store(in: &cancelBag) + + viewWillAppearSubject.send(()) + + + //then + wait(for: [expectation], timeout: 0.2) + XCTAssertEqual(data, ChallengeData.empty) + XCTAssertEqual(willOccureError, NetworkError.badCasting) + } + + func test_ChallengeVM의_leftButtonTapped가_올바른값을전달하고있는지() { + //given + let expectation = XCTestExpectation(description: "네비게이션왼쪽버튼이 눌렸을때") + + //when + var flowType: ChallengeViewModelImpl.FlowType! + self.viewModel.navigationSubject + .sink { flow in + flowType = flow + expectation.fulfill() + } + .store(in: &cancelBag) + + self.navigationLeftButtonTapped.send(()) + //then + wait(for: [expectation], timeout: 0.3) + XCTAssertEqual(flowType, .bookmarkButtonTapped) + } + + func test_ChallengeVM의_rightButtonTapped가_올바른값을전달하고있는지() { + //given + let expectation = XCTestExpectation(description: "네비게이션오른쪽버튼이 눌렸을때") + + //when + var flowType: ChallengeViewModelImpl.FlowType! + self.viewModel.navigationSubject + .sink { flow in + flowType = flow + expectation.fulfill() + } + .store(in: &cancelBag) + + self.navigationRightButtonTapped.send(()) + + //then + wait(for: [expectation], timeout: 0.3) + XCTAssertEqual(flowType, .myPageButtonTapped) + } +} diff --git a/LionHeart-iOS/LionHeart-iOSTests/LionHeart_iOSTests.swift b/LionHeart-iOS/LionHeart-iOSTests/LionHeart_iOSTests.swift deleted file mode 100644 index ac2ddd9f..00000000 --- a/LionHeart-iOS/LionHeart-iOSTests/LionHeart_iOSTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// LionHeart_iOSTests.swift -// LionHeart-iOSTests -// -// Created by 김민재 on 2023/07/05. -// - -import XCTest -@testable import LionHeart_iOS - -final class LionHeart_iOSTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -}