diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index cb582f5..924ed0b 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -108,6 +108,8 @@ 19AE48182B28C2B700DD4612 /* SettingInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19AE48142B28C2B700DD4612 /* SettingInteractorTests.swift */; }; 19AE481A2B28C2B700DD4612 /* SettingPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19AE48162B28C2B700DD4612 /* SettingPresenterTests.swift */; }; 19AE481C2B28C53800DD4612 /* MockSettingWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19AE481B2B28C53800DD4612 /* MockSettingWorker.swift */; }; + 19AE48232B29D03D00DD4612 /* EditProfileInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19AE481F2B29D03D00DD4612 /* EditProfileInteractorTests.swift */; }; + 19AE48252B29D03D00DD4612 /* EditProfilePresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19AE48212B29D03D00DD4612 /* EditProfilePresenterTests.swift */; }; 19C7AFCE2B02410F003B35F2 /* AuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19C7AFCD2B02410F003B35F2 /* AuthManager.swift */; }; 19C7AFD62B02584D003B35F2 /* KeychainStored.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19C7AFD52B02584D003B35F2 /* KeychainStored.swift */; }; 19E79AC02B0A85D0009EA9ED /* LoopingPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E79ABF2B0A85D0009EA9ED /* LoopingPlayerView.swift */; }; @@ -198,7 +200,6 @@ FC4E0C1D2B28977000152596 /* CurrentLocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4E0C1C2B28977000152596 /* CurrentLocationManager.swift */; }; FC4E0C202B28B4C500152596 /* MockLocationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4E0C1F2B28B4C500152596 /* MockLocationFetcher.swift */; }; FC5BE11C2B148D160036366D /* EditProfilePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE1162B148D160036366D /* EditProfilePresenter.swift */; }; - FC5BE11D2B148D160036366D /* EditProfileWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE1172B148D160036366D /* EditProfileWorker.swift */; }; FC5BE11E2B148D160036366D /* EditProfileRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE1182B148D160036366D /* EditProfileRouter.swift */; }; FC5BE11F2B148D160036366D /* EditProfileModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE1192B148D160036366D /* EditProfileModels.swift */; }; FC5BE1202B148D170036366D /* EditProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE11A2B148D160036366D /* EditProfileViewController.swift */; }; @@ -358,6 +359,8 @@ 19AE48142B28C2B700DD4612 /* SettingInteractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingInteractorTests.swift; sourceTree = ""; }; 19AE48162B28C2B700DD4612 /* SettingPresenterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingPresenterTests.swift; sourceTree = ""; }; 19AE481B2B28C53800DD4612 /* MockSettingWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSettingWorker.swift; sourceTree = ""; }; + 19AE481F2B29D03D00DD4612 /* EditProfileInteractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileInteractorTests.swift; sourceTree = ""; }; + 19AE48212B29D03D00DD4612 /* EditProfilePresenterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfilePresenterTests.swift; sourceTree = ""; }; 19C7AFCD2B02410F003B35F2 /* AuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthManager.swift; sourceTree = ""; }; 19C7AFD52B02584D003B35F2 /* KeychainStored.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStored.swift; sourceTree = ""; }; 19E79ABF2B0A85D0009EA9ED /* LoopingPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopingPlayerView.swift; sourceTree = ""; }; @@ -451,7 +454,6 @@ FC4E0C1C2B28977000152596 /* CurrentLocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentLocationManager.swift; sourceTree = ""; }; FC4E0C1F2B28B4C500152596 /* MockLocationFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLocationFetcher.swift; sourceTree = ""; }; FC5BE1162B148D160036366D /* EditProfilePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfilePresenter.swift; sourceTree = ""; }; - FC5BE1172B148D160036366D /* EditProfileWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileWorker.swift; sourceTree = ""; }; FC5BE1182B148D160036366D /* EditProfileRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileRouter.swift; sourceTree = ""; }; FC5BE1192B148D160036366D /* EditProfileModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileModels.swift; sourceTree = ""; }; FC5BE11A2B148D160036366D /* EditProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileViewController.swift; sourceTree = ""; }; @@ -609,6 +611,7 @@ 194C21C72B1DF09B00C62645 /* Scenes */ = { isa = PBXGroup; children = ( + 19AE481D2B29D02700DD4612 /* EditProfile */, 19AE48122B28C2A800DD4612 /* Setting */, 192513632B26F7BB001533FA /* TagPlayList */, 1925137B2B277CC4001533FA /* Profile */, @@ -766,6 +769,15 @@ path = Setting; sourceTree = ""; }; + 19AE481D2B29D02700DD4612 /* EditProfile */ = { + isa = PBXGroup; + children = ( + 19AE481F2B29D03D00DD4612 /* EditProfileInteractorTests.swift */, + 19AE48212B29D03D00DD4612 /* EditProfilePresenterTests.swift */, + ); + path = EditProfile; + sourceTree = ""; + }; 19BB8A572B07BEE30070B922 /* UIComponents */ = { isa = PBXGroup; children = ( @@ -967,7 +979,6 @@ FC5BE1162B148D160036366D /* EditProfilePresenter.swift */, FC5BE1192B148D160036366D /* EditProfileModels.swift */, FC5BE1182B148D160036366D /* EditProfileRouter.swift */, - FC5BE1172B148D160036366D /* EditProfileWorker.swift */, FC5BE1222B1490660036366D /* EditProfileConfigurator.swift */, ); path = EditProfile; @@ -1404,7 +1415,6 @@ 1945520D2B0399E500299768 /* MainTabBarViewController.swift in Sources */, FC2511AB2B04EA6B004717BC /* MapConfigurator.swift in Sources */, 1945523B2B05258200299768 /* HomeConfigurator.swift in Sources */, - FC5BE11D2B148D160036366D /* EditProfileWorker.swift in Sources */, 19A1693A2B17BCC400DB34C0 /* MemberDTO.swift in Sources */, 194551F62B037F2D00299768 /* LoginViewController.swift in Sources */, FC767FA52B125F430088CF9B /* UIViewController+.swift in Sources */, @@ -1559,6 +1569,7 @@ 192513A72B278BB3001533FA /* Seeds.swift in Sources */, 194C21C52B1DEE6B00C62645 /* HomeWorkerTests.swift in Sources */, 19AE481C2B28C53800DD4612 /* MockSettingWorker.swift in Sources */, + 19AE48252B29D03D00DD4612 /* EditProfilePresenterTests.swift in Sources */, 192513802B277CD7001533FA /* ProfileViewControllerTests.swift in Sources */, 194C21C62B1DEE6B00C62645 /* HomePresenterTests.swift in Sources */, FC4E0C202B28B4C500152596 /* MockLocationFetcher.swift in Sources */, @@ -1568,6 +1579,7 @@ 19AE48172B28C2B700DD4612 /* SettingViewControllerTests.swift in Sources */, 194C21C32B1DEE6B00C62645 /* HomeViewControllerTests.swift in Sources */, 192513692B26F7CE001533FA /* TagPlayListInteractorTests.swift in Sources */, + 19AE48232B29D03D00DD4612 /* EditProfileInteractorTests.swift in Sources */, 194C21CC2B1DF39200C62645 /* MockHomeWorker.swift in Sources */, FC4E0C0E2B282AE500152596 /* UploadPostWorkerTests.swift in Sources */, FC4E0C112B28595200152596 /* MockUploadPostWorker.swift in Sources */, diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift index 9343d1c..12baa56 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift @@ -13,6 +13,7 @@ import OSLog protocol EditProfileBusinessLogic { func setProfile(with request: EditProfileModels.SetProfile.Request) func changeProfile(with request: EditProfileModels.ChangeProfile.Request) + @discardableResult func checkDuplication(with request: EditProfileModels.CheckNicknameDuplication.Request) async -> Bool @discardableResult func editProfile(with request: EditProfileModels.EditProfile.Request) async -> Bool @@ -96,6 +97,7 @@ final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStor presenter?.presentProfileState(with: response) } + @discardableResult func checkDuplication(with request: Models.CheckNicknameDuplication.Request) async -> Bool { guard let response = await userWorker?.checkNotDuplication(for: request.nickname) else { os_log(.error, log: .data, "checkDuplication Server Error") diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileWorker.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileWorker.swift deleted file mode 100644 index 653c75c..0000000 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileWorker.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// EditProfileWorker.swift -// Layover -// -// Created by kong on 2023/11/27. -// Copyright © 2023 CodeBomber. All rights reserved. -// - -import UIKit - -final class EditProfileWorker { - - // MARK: - Properties - - typealias Models = EditProfileModels - - // MARK: - Methods - -} diff --git a/iOS/Layover/LayoverTests/Scenes/EditProfile/EditProfileInteractorTests.swift b/iOS/Layover/LayoverTests/Scenes/EditProfile/EditProfileInteractorTests.swift new file mode 100644 index 0000000..7af3360 --- /dev/null +++ b/iOS/Layover/LayoverTests/Scenes/EditProfile/EditProfileInteractorTests.swift @@ -0,0 +1,308 @@ +// +// EditProfileInteractorTests.swift +// Layover +// +// Created by 김인환 on 12/13/23. +// Copyright (c) 2023 CodeBomber. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +@testable import Layover +import XCTest + +final class EditProfileInteractorTests: XCTestCase { + // MARK: Subject under test + + var sut: EditProfileInteractor! + + let stubNickname = "안유진" + let stubIntroduce = "아이브의 리더 안유진" + let stubProfileImageData = Seeds.sampleImageData + + typealias Models = EditProfileModels + + // MARK: - Test lifecycle + + override func setUp() { + super.setUp() + setupEditProfileInteractor() + } + + override func tearDown() { + super.tearDown() + } + + // MARK: - Test setup + + func setupEditProfileInteractor() { + sut = EditProfileInteractor() + sut.userWorker = MockUserWorker() + sut.nickname = stubNickname + sut.introduce = stubIntroduce + sut.profileImageData = stubProfileImageData + } + + // MARK: - Test doubles + + final class EditProfilePresentationLogicSpy: EditProfilePresentationLogic { + var presentSetProfileCalled = false + var presentSetProfileResponse: Models.SetProfile.Response! + var presentEditProfileCalled = false + var presentProfileStateCalled = false + var presentProfileStateResponse: Models.ChangeProfile.Response! + var presentNicknameDuplicationCalled = false + var presentNicknameDuplicationResponse: Models.CheckNicknameDuplication.Response! + + func presentProfile(with response: Models.SetProfile.Response) { + presentSetProfileCalled = true + presentSetProfileResponse = response + } + + func presentProfile(with response: Models.EditProfile.Response) { + presentEditProfileCalled = true + } + + func presentProfileState(with response: Models.ChangeProfile.Response) { + presentProfileStateCalled = true + presentProfileStateResponse = response + } + + func presentNicknameDuplication(with response: Models.CheckNicknameDuplication.Response) { + presentNicknameDuplicationCalled = true + presentNicknameDuplicationResponse = response + } + } + + final class UserWorkerMock: UserWorker { + override func checkNotDuplication(for userName: String) async -> Bool? { + return false + } + } + + // MARK: - Tests + + func test_setProfile을_실행하면_presenter의_presentProfile이_호출되고_자신의_데이터를_presenter에게_전달한다() { + // arrange + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.setProfile(with: Models.SetProfile.Request()) + + // assert + XCTAssertTrue(spy.presentSetProfileCalled, "setProfile은 presenter의 presentProfile을 호출하지 못했다.") + XCTAssertEqual(spy.presentSetProfileResponse.nickname, stubNickname, "setProfile은 presenter에게 nickname을 전달하지 못했다.") + XCTAssertEqual(spy.presentSetProfileResponse.introduce, stubIntroduce, "setProfile은 presenter에게 introduce를 전달하지 못했다.") + XCTAssertEqual(spy.presentSetProfileResponse.profileImageData, stubProfileImageData, "setProfile은 presenter에게 profileImageData를 전달하지 못했다.") + } + + func test_changeProfile을_실행했을때_presenter의_presentProfileState가_호출되고_전달된_nickname이_2자_이상_8자_이하의_한글영숫자로만_이루어진_닉네임이면_presenter에_nicknameState를_valid로_전달하고_canEditProfile을_True로_전달하고_기존_닉네임과_다르면_canCheckNicknameDuplicate를_true로_전달한다() { + // arrange + let changedNickname = "안유진an12" + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.changeProfile(with: Models.ChangeProfile.Request(changedProfileComponent: .nickname(changedNickname))) + + // assert + XCTAssertTrue(spy.presentProfileStateCalled, "changeProfile을 호출해서 presenter의 presentProfileState를 호출하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.nicknameState, .valid, "changeProfile을 호출해서 presenter에게 올바른 nickname의 nicknameState를 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canEditProfile, "changeProfile을 호출해서 presenter에게 올바른 canEditProfile 값을 전달하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.introduceAlertDescription, "presenter에게 올바른 introduceAlertDescription을 전달하지 못했다.") + XCTAssertTrue(spy.presentProfileStateResponse.canCheckNicknameDuplication!, "changeProfile을 호출해서 presenter에게 anCheckNicknameDuplication을 true로 전달하지 못했다.") + } + + func test_changeProfile을_실행했을때_presenter의_presentProfileState가_호출되고_전달된_nickname이_2자_이상_8자_이하의_한글영숫자로만_이루어진_닉네임이면_presenter에_nicknameState를_valid로_전달하고_기존_닉네임과_같으면_canEditProfile을_false로_전달하고_canCheckNicknameDuplicate를_false로_전달한다() { + // arrange + let changedNickname = stubNickname + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.changeProfile(with: Models.ChangeProfile.Request(changedProfileComponent: .nickname(changedNickname))) + + // assert + XCTAssertTrue(spy.presentProfileStateCalled, "changeProfile을 호출해서 presenter의 presentProfileState를 호출하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.nicknameState, .valid, "changeProfile을 호출해서 presenter에게 올바른 nickname의 nicknameState를 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canEditProfile, "changeProfile을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.introduceAlertDescription, "presenter에게 올바른 introduceAlertDescription을 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canCheckNicknameDuplication!, "changeProfile을 호출해서 presenter에게 올바른 canCheckNicknameDuplication을 전달하지 못했다.") + } + + + func test_changeProfile을_실행했을때_presenter의_presentProfileState가_호출되고_전달된_nickname이_2자_이상_8자_이하로_이루어진_한글영숫자로만_이루어지지_않은_다른_닉네임이면_presenter에_nicknameState를_invalidCharacter로_전달한다() { + // arrange + let changedNickname = "유진jin😭" + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.changeProfile(with: Models.ChangeProfile.Request(changedProfileComponent: .nickname(changedNickname))) + + // assert + XCTAssertTrue(spy.presentProfileStateCalled, "changeProfile을 호출해서 presenter의 presentProfileState를 호출하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.nicknameState, .invalidCharacter, "changeProfile을 호출해서 presenter에게 올바른 nickname의 nicknameState를 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canEditProfile, "changeProfile을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.introduceAlertDescription, "presenter에게 올바른 introduceAlertDescription을 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canCheckNicknameDuplication!, "changeProfile을 호출해서 presenter에게 올바른 canCheckNicknameDuplication을 전달하지 못했다.") + } + + func test_changeProfile을_실행했을때_presenter의_presentProfileState가_호출되고_전달된_nickname이_2자_미만_으로_이루어진_한글로만_이루어진_닉네임이면_presenter에_nicknameState를_lessThan2GreaterThan8로_전달한다() { + // arrange + let changedNickname = "안" + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.changeProfile(with: Models.ChangeProfile.Request(changedProfileComponent: .nickname(changedNickname))) + + // assert + XCTAssertTrue(spy.presentProfileStateCalled, "changeProfile을 호출해서 presenter의 presentProfileState를 호출하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.nicknameState, .lessThan2GreaterThan8, "changeProfile을 호출해서 presenter에게 올바른 nickname의 nicknameState를 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canEditProfile, "changeProfile을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.introduceAlertDescription, "presenter에게 올바른 introduceAlertDescription을 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canCheckNicknameDuplication!, "changeProfile을 호출해서 presenter에게 올바른 canCheckNicknameDuplication을 전달하지 못했다.") + } + func test_changeProfile을_실행했을때_presenter의_presentProfileState가_호출되고_전달된_nickname이_2자_미만_으로_이루어진_영어로만_이루어진_닉네임이면_presenter에_nicknameState를_lessThan2GreaterThan8로_전달한다() { + // arrange + let changedNickname = "a" + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.changeProfile(with: Models.ChangeProfile.Request(changedProfileComponent: .nickname(changedNickname))) + + // assert + XCTAssertTrue(spy.presentProfileStateCalled, "changeProfile을 호출해서 presenter의 presentProfileState를 호출하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.nicknameState, .lessThan2GreaterThan8, "changeProfile을 호출해서 presenter에게 올바른 nickname의 nicknameState를 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canEditProfile, "changeProfile을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.introduceAlertDescription, "presenter에게 올바른 introduceAlertDescription을 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canCheckNicknameDuplication!, "changeProfile을 호출해서 presenter에게 올바른 canCheckNicknameDuplication을 전달하지 못했다.") + } + + func test_changeProfile을_실행했을때_presenter의_presentProfileState가_호출되고_전달된_nickname이_8자_이상_으로_이루어진_한글영숫자로만_이루어진_닉네임이면_presenter에_nicknameState를_lessThan2GreaterThan8로_전달한다() { + // arrange + let changedNickname = "안유진안댕댕good123" + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.changeProfile(with: Models.ChangeProfile.Request(changedProfileComponent: .nickname(changedNickname))) + + // assert + XCTAssertTrue(spy.presentProfileStateCalled, "changeProfile을 호출해서 presenter의 presentProfileState를 호출하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.nicknameState, .lessThan2GreaterThan8, "changeProfile을 호출해서 presenter에게 올바른 nickname의 nicknameState를 lessThan2GreaterThan8로 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canEditProfile, "changeProfile을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.introduceAlertDescription, "presenter에게 올바른 introduceAlertDescription을 전달하지 못했다.") + XCTAssertFalse(spy.presentProfileStateResponse.canCheckNicknameDuplication!, "changeProfile을 호출해서 presenter에게 올바른 canCheckNicknameDuplication을 전달하지 못했다.") + } + + func test_changeProfile을_실행했을때_presenter의_presentProfileState가_호출되고_전달된_introduce의_길이가_introduceLengthLimit보다_짧으면_presenter에_introduceAlertDescription이_nil_로_전달된다() { + // arrange + let changedIntroduce = "안녕하세요 아이브의 리더 안유진입니다." + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.changeProfile(with: Models.ChangeProfile.Request(changedProfileComponent: .introduce(changedIntroduce))) + + // assert + XCTAssertTrue(spy.presentProfileStateCalled, "changeProfile을 호출해서 presenter의 presentProfileState를 호출하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.introduceAlertDescription, "changeProfile을 호출해서 presenter에게 introduceAlertDescription이 Nil로 전달하지 못했다.") + XCTAssertTrue(spy.presentProfileStateResponse.canEditProfile, "changeProfile을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.canCheckNicknameDuplication, "changeProfile을 호출해서 presenter에게 올바른 canCheckNicknameDuplication을 전달하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.nicknameState, .valid, "changeProfile을 호출해서 presenter에게 올바른 nickname의 nicknameState를 전달하지 못했다.") + } + + func test_changeProfile을_실행했을때_presenter의_presentProfileState가_호출되고_전달된_introduce의_길이가_ChangeProfile_introduceLengthLimit보다_길면_presenter에_introduceAlertDescription이_IntroduceLengthState_overLength의_description_으로_전달된다() { + // arrange + let changedIntroduce = "안녕하세요 아이브의 리더 안유진입니다.안녕하세요 아이브의 리더 안유진입니다.안녕하세요 아이브의 리더 안유진입니다.안녕하세요 아이브의 리더 안유진입니다." + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.changeProfile(with: Models.ChangeProfile.Request(changedProfileComponent: .introduce(changedIntroduce))) + + // assert + XCTAssertTrue(spy.presentProfileStateCalled, "changeProfile을 호출해서 presenter의 presentProfileState를 호출하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.introduceAlertDescription, Models.ChangeProfile.IntroduceLengthState.overLength.description,"changeProfile을 호출해서 presenter에게 introduceAlertDescription이 Nil로 전달하지 못했다.") + + XCTAssertFalse(spy.presentProfileStateResponse.canEditProfile, "changeProfile을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.canCheckNicknameDuplication, "changeProfile을 호출해서 presenter에게 올바른 canCheckNicknameDuplication을 전달하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.nicknameState, .valid, "changeProfile을 호출해서 presenter에게 올바른 nickname의 nicknameState를 전달하지 못했다.") + } + + func test_changeProfile을_실행했을때_presenter의_presentProfileState가_호출되고_전달된_introduce가_원래의_introduce와_같으면_presenter에_introduceAlertDescription이_nil로_전달된다() { + // arrange + let changedIntroduce = "안녕하세요 아이브의 리더 안유진입니다." + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.changeProfile(with: Models.ChangeProfile.Request(changedProfileComponent: .introduce(changedIntroduce))) + + // assert + XCTAssertTrue(spy.presentProfileStateCalled, "changeProfile을 호출해서 presenter의 presentProfileState를 호출하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.introduceAlertDescription, "changeProfile을 호출해서 presenter에게 introduceAlertDescription이 Nil로 전달하지 못했다.") + + XCTAssertTrue(spy.presentProfileStateResponse.canEditProfile, "changeProfile을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.canCheckNicknameDuplication, "changeProfile을 호출해서 presenter에게 올바른 canCheckNicknameDuplication을 전달하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.nicknameState, .valid, "changeProfile을 호출해서 presenter에게 올바른 nickname의 nicknameState를 전달하지 못했다.") + } + + func test_changeProfile을_실행했을때_presenter의_presentProfileState가_호출되고_전달된_값이_profileImage라면_이전의_상태값들이_전달된다() { + + // arrange + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + sut.changeProfile(with: Models.ChangeProfile.Request(changedProfileComponent: .profileImage(nil))) + + // assert + XCTAssertTrue(spy.presentProfileStateCalled, "changeProfile을 호출해서 presenter의 presentProfileState를 호출하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.introduceAlertDescription, "changeProfile을 호출해서 presenter에게 introduceAlertDescription이 Nil로 전달하지 못했다.") + XCTAssertTrue(spy.presentProfileStateResponse.canEditProfile, "changeProfile을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.") + XCTAssertNil(spy.presentProfileStateResponse.canCheckNicknameDuplication, "changeProfile을 호출해서 presenter에게 올바른 canCheckNicknameDuplication을 전달하지 못했다.") + XCTAssertEqual(spy.presentProfileStateResponse.nicknameState, .valid, "changeProfile을 호출해서 presenter에게 올바른 nickname의 nicknameState를 전달하지 못했다.") + } + + func test_checkDuplication을_실행했을때_중복된_닉네임이_아니라면_presenter의_presentNicknameDuplication을_호출하고_올바른_값을_전달한다() async { + // arrange + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + + // act + await sut.checkDuplication(with: Models.CheckNicknameDuplication.Request(nickname: "안유진")) + + // assert + XCTAssertTrue(spy.presentNicknameDuplicationCalled, "checkDuplication을 호출해서 presenter의 presentNicknameDuplication을 호출하지 못했다.") + XCTAssertTrue(spy.presentNicknameDuplicationResponse.isValid, + "checkDuplication을 호출해서 presenter에게 올바른 isValid값을 전달하지 못했다.") + XCTAssertTrue(spy.presentNicknameDuplicationResponse.canEditProfile, + "checkDuplication을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.") + } + + func test_checkDuplication을_실행했을때_중복된_닉네임이라면_presenter의_presentNicknameDuplication을_호출하고_올바른_값을_전달한다() async { + + // arrange + let spy = EditProfilePresentationLogicSpy() + sut.presenter = spy + sut.userWorker = UserWorkerMock() + + // act + await sut.checkDuplication(with: Models.CheckNicknameDuplication.Request(nickname: "안유진")) + + // assert + XCTAssertTrue(spy.presentNicknameDuplicationCalled, "checkDuplication을 호출해서 presenter의 presentNicknameDuplication을 호출하지 못했다.") + XCTAssertFalse(spy.presentNicknameDuplicationResponse.isValid, + "checkDuplication을 호출해서 presenter에게 올바른 isValid값을 전달하지 못했다.") + XCTAssertFalse(spy.presentNicknameDuplicationResponse.canEditProfile, + "checkDuplication을 호출해서 presenter에게 올바른 canEditProfile값을 전달하지 못했다.")} + + // TODO: editProfile 테스트 메서드는 내부 병렬실행 이슈 해결 후 작성 +} diff --git a/iOS/Layover/LayoverTests/Scenes/EditProfile/EditProfilePresenterTests.swift b/iOS/Layover/LayoverTests/Scenes/EditProfile/EditProfilePresenterTests.swift new file mode 100644 index 0000000..2cdb1b2 --- /dev/null +++ b/iOS/Layover/LayoverTests/Scenes/EditProfile/EditProfilePresenterTests.swift @@ -0,0 +1,162 @@ +// +// EditProfilePresenterTests.swift +// Layover +// +// Created by 김인환 on 12/13/23. +// Copyright (c) 2023 CodeBomber. All rights reserved. +// +// This file was generated by the Clean Swift Xcode Templates so +// you can apply clean architecture to your iOS and Mac projects, +// see http://clean-swift.com +// + +@testable import Layover +import XCTest + +final class EditProfilePresenterTests: XCTestCase { + // MARK: Subject under test + + var sut: EditProfilePresenter! + + typealias Models = EditProfileModels + + // MARK: - Test lifecycle + + override func setUp() { + super.setUp() + setupEditProfilePresenter() + } + + override func tearDown() { + super.tearDown() + } + + // MARK: - Test setup + + func setupEditProfilePresenter() { + sut = EditProfilePresenter() + } + + // MARK: - Test doubles + + final class EditProfileDisplayLogicSpy: EditProfileDisplayLogic { + var displayProfileCalled = false + var displayProfileViewModel: Models.SetProfile.ViewModel! + var displayProfileEditCompletedCalled = false + var displayProfileEditCompletedViewModel: Models.EditProfile.ViewModel! + var displayChangedProfileStateCalled = false + var displayChangedProfileStateViewModel: Models.ChangeProfile.ViewModel! + var displayNicknameDuplicationCalled = false + var displayNicknameDuplicationViewModel: Models.CheckNicknameDuplication.ViewModel! + + func displayProfile(with viewModel: Models.SetProfile.ViewModel) { + displayProfileCalled = true + displayProfileViewModel = viewModel + } + + func displayProfileEditCompleted(with viewModel: Models.EditProfile.ViewModel) { + displayProfileEditCompletedCalled = true + displayProfileEditCompletedViewModel = viewModel + } + + func displayChangedProfileState(with viewModel: Models.ChangeProfile.ViewModel) { + displayChangedProfileStateCalled = true + displayChangedProfileStateViewModel = viewModel + } + + func displayNicknameDuplication(with viewModel: Models.CheckNicknameDuplication.ViewModel) { + displayNicknameDuplicationCalled = true + displayNicknameDuplicationViewModel = viewModel + + } + } + + // MARK: - Tests + + func test_presentProfile을_실행하면_viewController의_displayProfile가_호출되고_올바른_값을_전달한다() { + // arrange + let spy = EditProfileDisplayLogicSpy() + sut.viewController = spy + let nickname = "안유진" + let introduce = "아이브의 리더" + let profileImageData = Seeds.sampleImageData + let response = Models.SetProfile.Response(nickname: nickname, + introduce: introduce, + profileImageData: profileImageData) + + // act + sut.presentProfile(with: response) + + // assert + XCTAssertTrue(spy.displayProfileCalled, "presentProfile을 실행해서 displayProfile이 호출되지 못했다") + XCTAssertEqual(spy.displayProfileViewModel.nickname, nickname, "presentProfile을 실행해서 올바른 nickname이 전달되지 못했다.") + XCTAssertEqual(spy.displayProfileViewModel.introduce, introduce, "presentProfile을 실행해서 올바른 introduce가 전달되지 못했다.") + XCTAssertEqual(spy.displayProfileViewModel.profileImageData, profileImageData, "presentProfile을 실행해서 올바른 profileImageData가 전달되지 못했다.") + } + + func test_presentProfile을_실행하면_viewController의_displayProfileEditCompleted가_호출되고_isSuccess값이_true이면_toastMessage로_프로필이_수정되었습니다_를_전달한다() { + // arrange + let spy = EditProfileDisplayLogicSpy() + sut.viewController = spy + let response = Models.EditProfile.Response(isSuccess: true) + + // act + sut.presentProfile(with: response) + + // assert + XCTAssertTrue(spy.displayProfileEditCompletedCalled, "presentProfileEditCompleted을 실행해서 displayProfileEditCompleted이 호출되지 못했다") + XCTAssertEqual(spy.displayProfileEditCompletedViewModel.toastMessage, "프로필이 수정되었습니다.", "presentProfileEditCompleted을 실행해서 올바른 toastMessage가 전달되지 못했다.") + } + + func test_presentProfile을_실행하면_viewController의_displayProfileEditCompleted가_호출되고_isSuccess값이_false면_toastMessage로_프로필_수정에_실패했습니다_를_전달한다() { + // arrange + let spy = EditProfileDisplayLogicSpy() + sut.viewController = spy + let response = Models.EditProfile.Response(isSuccess: false) + + // act + sut.presentProfile(with: response) + + // assert + XCTAssertTrue(spy.displayProfileEditCompletedCalled, "presentProfileEditCompleted을 실행해서 displayProfileEditCompleted이 호출되지 못했다") + XCTAssertEqual(spy.displayProfileEditCompletedViewModel.toastMessage, "프로필 수정에 실패했습니다.", "presentProfileEditCompleted을 실행해서 올바른 toastMessage가 전달되지 못했다.") + } + + func test_presentProfileState를_실행하면_viewController의_displayChangedProfileState가_호출되고_올바른_값을_전달한다() { + // arrange + let spy = EditProfileDisplayLogicSpy() + sut.viewController = spy + let response = Models.ChangeProfile.Response(nicknameState: .lessThan2GreaterThan8, + nicknameAlertDescription: "닉네임 알림", + introduceAlertDescription: "소개글 알림", + canCheckNicknameDuplication: true, + canEditProfile: true) + + // act + sut.presentProfileState(with: response) + + // assert + XCTAssertTrue(spy.displayChangedProfileStateCalled, "presentProfileState을 실행해서 displayChangedProfileState이 호출되지 못했다") + XCTAssertEqual(spy.displayChangedProfileStateViewModel.nicknameState, .lessThan2GreaterThan8, "presentProfileState을 실행해서 올바른 nicknameState가 전달되지 못했다.") + XCTAssertEqual(spy.displayChangedProfileStateViewModel.nicknameAlertDescription, "닉네임 알림", "presentProfileState을 실행해서 올바른 nicknameAlertDescription가 전달되지 못했다.") + XCTAssertEqual(spy.displayChangedProfileStateViewModel.introduceAlertDescription, "소개글 알림", "presentProfileState을 실행해서 올바른 introduceAlertDescription가 전달되지 못했다.") + XCTAssertEqual(spy.displayChangedProfileStateViewModel.canCheckNicknameDuplication, true, "presentProfileState을 실행해서 올바른 canCheckNicknameDuplication가 전달되지 못했다.") + XCTAssertEqual(spy.displayChangedProfileStateViewModel.canEditProfile, true, "presentProfileState을 실행해서 올바른 canEditProfile이 전달되지 못했다.") + } + + func test_presentNicknameDuplication을_실행하면_viewController의_displayNicknameDuplication이_호출되고_올바른_값을_전달한다() { + // arrange + let spy = EditProfileDisplayLogicSpy() + sut.viewController = spy + let response = Models.CheckNicknameDuplication.Response(isValid: true, + canEditProfile: true) + + // act + sut.presentNicknameDuplication(with: response) + + // assert + XCTAssertTrue(spy.displayNicknameDuplicationCalled, "presentNicknameDuplication을 실행해서 displayNicknameDuplication이 호출되지 못했다") + XCTAssertEqual(spy.displayNicknameDuplicationViewModel.isValidNickname, true, "presentNicknameDuplication을 실행해서 올바른 isValidNickname이 전달되지 못했다.") + XCTAssertEqual(spy.displayNicknameDuplicationViewModel.canEditProfile, true, "presentNicknameDuplication을 실행해서 올바른 canEditProfile이 전달되지 못했다.") + } +}