From 2d570307282f76bf9cb2d9b297b13878b9a716d3 Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Wed, 3 Jan 2024 17:50:26 +0900 Subject: [PATCH 01/16] =?UTF-8?q?:recycle:=20Task=EA=B0=90=EC=8B=B8?= =?UTF-8?q?=EB=8A=94=EA=B1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover.xcodeproj/project.pbxproj | 1 - .../Network/Mock/MockData/PostList.json | 8 ++-- .../Scenes/Home/HomeConfigurator.swift | 3 +- .../Scenes/Login/LoginConfigurator.swift | 3 +- .../Playback/PlaybackConfigurator.swift | 3 +- .../Scenes/Playback/PlaybackInteractor.swift | 47 ++++++++++++------- .../Playback/PlaybackViewController.swift | 4 +- 7 files changed, 43 insertions(+), 26 deletions(-) diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index 1903fab..3455019 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -1436,7 +1436,6 @@ FC2511AB2B04EA6B004717BC /* MapConfigurator.swift in Sources */, 1945523B2B05258200299768 /* HomeConfigurator.swift in Sources */, FCF19BE22B2A4088003002E0 /* AVFileType+.swift in Sources */, - FC5BE11D2B148D160036366D /* EditProfileWorker.swift in Sources */, 19A1693A2B17BCC400DB34C0 /* MemberDTO.swift in Sources */, 194551F62B037F2D00299768 /* LoginViewController.swift in Sources */, FC767FA52B125F430088CF9B /* UIViewController+.swift in Sources */, diff --git a/iOS/Layover/Layover/Network/Mock/MockData/PostList.json b/iOS/Layover/Layover/Network/Mock/MockData/PostList.json index 7edcbf7..74cd412 100644 --- a/iOS/Layover/Layover/Network/Mock/MockData/PostList.json +++ b/iOS/Layover/Layover/Network/Mock/MockData/PostList.json @@ -81,8 +81,8 @@ { "member" : { "id" : 5, - "username" : "layover", - "introduce" : "Hi, my name is hwani", + "username" : "layover2", + "introduce" : "Hi, my name is hwani2", "profile_image_url" : "profile_image_link" }, "board" : { @@ -91,8 +91,8 @@ "video_thumbnail_url" : "https://res.heraldm.com/content/image/2023/04/16/20230416000040_0.jpg", "longitude" : 37.0532156213, "latitude" : 127.060123123, - "title" : "아이즈원", - "content" : "게시글 설명2", + "title" : "아이즈원2", + "content" : "게시글 설명3", "status": "COMPLETE" }, "tag" : ["해시태그1", "해시태그6"] diff --git a/iOS/Layover/Layover/Scenes/Home/HomeConfigurator.swift b/iOS/Layover/Layover/Scenes/Home/HomeConfigurator.swift index 340d6aa..60899fd 100644 --- a/iOS/Layover/Layover/Scenes/Home/HomeConfigurator.swift +++ b/iOS/Layover/Layover/Scenes/Home/HomeConfigurator.swift @@ -18,7 +18,8 @@ final class HomeConfigurator: Configurator { let router = HomeRouter() let presenter = HomePresenter() let interactor = HomeInteractor() - let homeWorker = HomeWorker() +// let homeWorker = HomeWorker() + let homeWorker = MockHomeWorker() let videoFileWorker = VideoFileWorker() let locationManager = CurrentLocationManager() diff --git a/iOS/Layover/Layover/Scenes/Login/LoginConfigurator.swift b/iOS/Layover/Layover/Scenes/Login/LoginConfigurator.swift index a8cb588..01bd7a4 100644 --- a/iOS/Layover/Layover/Scenes/Login/LoginConfigurator.swift +++ b/iOS/Layover/Layover/Scenes/Login/LoginConfigurator.swift @@ -20,7 +20,8 @@ final class LoginConfigurator: Configurator { let interactor = LoginInteractor() let presenter = LoginPresenter() // TODO: 실행 전 Worker Mock인지 확인 - let worker = LoginWorker() +// let worker = LoginWorker() + let worker = MockLoginWorker() let router = LoginRouter() router.viewController = viewController diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackConfigurator.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackConfigurator.swift index c70f516..9e45e24 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackConfigurator.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackConfigurator.swift @@ -19,7 +19,8 @@ final class PlaybackConfigurator: Configurator { let viewController: PlaybackViewController = viewController let interactor: PlaybackInteractor = PlaybackInteractor() let presenter: PlaybackPresenter = PlaybackPresenter() - let worker: PlaybackWorkerProtocol = PlaybackWorker() +// let worker: PlaybackWorkerProtocol = PlaybackWorker() + let worker: PlaybackWorkerProtocol = MockPlaybackWorker() let router: PlaybackRouter = PlaybackRouter() router.viewController = viewController diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift index b1a0153..aa01a04 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift @@ -10,7 +10,7 @@ import UIKit protocol PlaybackBusinessLogic { @discardableResult - func displayVideoList() -> Task + func displayVideoList() async func moveInitialPlaybackCell() func setInitialPlaybackCell() func leavePlaybackView() @@ -84,22 +84,35 @@ final class PlaybackInteractor: PlaybackBusinessLogic, PlaybackDataStore { // MARK: - UseCase Load Video List - func displayVideoList() -> Task { - Task { - guard let parentView: Models.ParentView, - var posts: [Post], - let worker: PlaybackWorkerProtocol else { return false } - if parentView == .map { - posts = worker.makeInfiniteScroll(posts: posts) - self.posts = posts - } - let videos: [Models.PlaybackVideo] - (videos, playbackVideoInfos) = transPostToVideo(posts) - let response: Models.LoadPlaybackVideoList.Response = Models.LoadPlaybackVideoList.Response(videos: videos) - await MainActor.run { - presenter?.presentVideoList(with: response) - } - return true + func displayVideoList() async { +// Task { +// guard let parentView: Models.ParentView, +// var posts: [Post], +// let worker: PlaybackWorkerProtocol else { return false } +// if parentView == .map { +// posts = worker.makeInfiniteScroll(posts: posts) +// self.posts = posts +// } +// let videos: [Models.PlaybackVideo] +// (videos, playbackVideoInfos) = transPostToVideo(posts) +// let response: Models.LoadPlaybackVideoList.Response = Models.LoadPlaybackVideoList.Response(videos: videos) +// await MainActor.run { +// presenter?.presentVideoList(with: response) +// } +// return true +// } + guard let parentView: Models.ParentView, + var posts: [Post], + let worker: PlaybackWorkerProtocol else { return } + if parentView == .map { + posts = worker.makeInfiniteScroll(posts: posts) + self.posts = posts + } + let videos: [Models.PlaybackVideo] + (videos, playbackVideoInfos) = transPostToVideo(posts) + let response: Models.LoadPlaybackVideoList.Response = Models.LoadPlaybackVideoList.Response(videos: videos) + await MainActor.run { + presenter?.presentVideoList(with: response) } } diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift index 9a62e6a..fd8eec8 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift @@ -92,7 +92,9 @@ final class PlaybackViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() - interactor?.displayVideoList() + Task { + await interactor?.displayVideoList() + } interactor?.configurePlaybackCell() playbackCollectionView.delegate = self playbackCollectionView.contentInsetAdjustmentBehavior = .never From bcd0934bd10e5290bc0b0180e011f78f8a630f69 Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:30:05 +0900 Subject: [PATCH 02/16] =?UTF-8?q?:recycle:=20PlaybackInteractor=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit return Task Type에서 async로 변경 --- .../Scenes/Home/HomeConfigurator.swift | 3 +- .../Scenes/Login/LoginConfigurator.swift | 3 +- .../Playback/PlaybackConfigurator.swift | 3 +- .../Scenes/Playback/PlaybackInteractor.swift | 111 +++++++----------- .../Playback/PlaybackViewController.swift | 16 ++- 5 files changed, 55 insertions(+), 81 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Home/HomeConfigurator.swift b/iOS/Layover/Layover/Scenes/Home/HomeConfigurator.swift index 60899fd..340d6aa 100644 --- a/iOS/Layover/Layover/Scenes/Home/HomeConfigurator.swift +++ b/iOS/Layover/Layover/Scenes/Home/HomeConfigurator.swift @@ -18,8 +18,7 @@ final class HomeConfigurator: Configurator { let router = HomeRouter() let presenter = HomePresenter() let interactor = HomeInteractor() -// let homeWorker = HomeWorker() - let homeWorker = MockHomeWorker() + let homeWorker = HomeWorker() let videoFileWorker = VideoFileWorker() let locationManager = CurrentLocationManager() diff --git a/iOS/Layover/Layover/Scenes/Login/LoginConfigurator.swift b/iOS/Layover/Layover/Scenes/Login/LoginConfigurator.swift index 01bd7a4..a8cb588 100644 --- a/iOS/Layover/Layover/Scenes/Login/LoginConfigurator.swift +++ b/iOS/Layover/Layover/Scenes/Login/LoginConfigurator.swift @@ -20,8 +20,7 @@ final class LoginConfigurator: Configurator { let interactor = LoginInteractor() let presenter = LoginPresenter() // TODO: 실행 전 Worker Mock인지 확인 -// let worker = LoginWorker() - let worker = MockLoginWorker() + let worker = LoginWorker() let router = LoginRouter() router.viewController = viewController diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackConfigurator.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackConfigurator.swift index 9e45e24..c70f516 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackConfigurator.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackConfigurator.swift @@ -19,8 +19,7 @@ final class PlaybackConfigurator: Configurator { let viewController: PlaybackViewController = viewController let interactor: PlaybackInteractor = PlaybackInteractor() let presenter: PlaybackPresenter = PlaybackPresenter() -// let worker: PlaybackWorkerProtocol = PlaybackWorker() - let worker: PlaybackWorkerProtocol = MockPlaybackWorker() + let worker: PlaybackWorkerProtocol = PlaybackWorker() let router: PlaybackRouter = PlaybackRouter() router.viewController = viewController diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift index aa01a04..58da271 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift @@ -9,7 +9,6 @@ import UIKit protocol PlaybackBusinessLogic { - @discardableResult func displayVideoList() async func moveInitialPlaybackCell() func setInitialPlaybackCell() @@ -31,10 +30,8 @@ protocol PlaybackBusinessLogic { func resumeVideo() func moveToProfile(with request: PlaybackModels.MoveToRelativeView.Request) func moveToTagPlay(with request: PlaybackModels.MoveToRelativeView.Request) - @discardableResult - func fetchPosts() -> Task - @discardableResult - func loadProfileImageAndLocation(with request: PlaybackModels.LoadProfileImageAndLocation.Request) -> Task + func fetchPosts() async + func loadProfileImageAndLocation(with request: PlaybackModels.LoadProfileImageAndLocation.Request) async } protocol PlaybackDataStore: AnyObject { @@ -85,22 +82,6 @@ final class PlaybackInteractor: PlaybackBusinessLogic, PlaybackDataStore { // MARK: - UseCase Load Video List func displayVideoList() async { -// Task { -// guard let parentView: Models.ParentView, -// var posts: [Post], -// let worker: PlaybackWorkerProtocol else { return false } -// if parentView == .map { -// posts = worker.makeInfiniteScroll(posts: posts) -// self.posts = posts -// } -// let videos: [Models.PlaybackVideo] -// (videos, playbackVideoInfos) = transPostToVideo(posts) -// let response: Models.LoadPlaybackVideoList.Response = Models.LoadPlaybackVideoList.Response(videos: videos) -// await MainActor.run { -// presenter?.presentVideoList(with: response) -// } -// return true -// } guard let parentView: Models.ParentView, var posts: [Post], let worker: PlaybackWorkerProtocol else { return } @@ -372,59 +353,51 @@ final class PlaybackInteractor: PlaybackBusinessLogic, PlaybackDataStore { presenter?.presentTagPlay() } - func fetchPosts() -> Task { - Task { - if !isFetchReqeust { - isFetchReqeust = true - var page: Int = 0 - if parentView != .home { - page = playbackVideoInfos.count / Models.fetchPostCount + 1 - if page == currentPage { - return false - } + func fetchPosts() async { + if !isFetchReqeust { + isFetchReqeust = true + var page: Int = 0 + if parentView != .home { + page = playbackVideoInfos.count / Models.fetchPostCount + 1 + if page == currentPage { + return } - currentPage = page - var newPosts: [Post]? - switch parentView { - case .home: - newPosts = await worker?.fetchHomePosts() - case .map: - return false - case .myProfile, .otherProfile: - newPosts = await worker?.fetchProfilePosts(profileID: memberID, page: page) - case .tag: - guard let selectedTag else { return false } - newPosts = await worker?.fetchTagPosts(selectedTag: selectedTag, page: page) - default: - return false - } - guard let newPosts else { return false } - posts?.append(contentsOf: newPosts) - let videos: [Models.PlaybackVideo] - let newInfos: [Models.PlaybackInfo] - (videos, newInfos) = transPostToVideo(newPosts) - self.playbackVideoInfos.append(contentsOf: newInfos) - let response: Models.LoadPlaybackVideoList.Response = Models.LoadPlaybackVideoList.Response(videos: videos) - await MainActor.run { - presenter?.presentLoadFetchVideos(with: response) - isFetchReqeust = false - } - return true } - return false + currentPage = page + var newPosts: [Post]? + switch parentView { + case .home: + newPosts = await worker?.fetchHomePosts() + case .map: + return + case .myProfile, .otherProfile: + newPosts = await worker?.fetchProfilePosts(profileID: memberID, page: page) + case .tag: + guard let selectedTag else { return } + newPosts = await worker?.fetchTagPosts(selectedTag: selectedTag, page: page) + default: + return + } + guard let newPosts else { return } + posts?.append(contentsOf: newPosts) + let videos: [Models.PlaybackVideo] + let newInfos: [Models.PlaybackInfo] + (videos, newInfos) = transPostToVideo(newPosts) + self.playbackVideoInfos.append(contentsOf: newInfos) + let response: Models.LoadPlaybackVideoList.Response = Models.LoadPlaybackVideoList.Response(videos: videos) + await MainActor.run { + presenter?.presentLoadFetchVideos(with: response) + isFetchReqeust = false + } } } - func loadProfileImageAndLocation(with request: PlaybackModels.LoadProfileImageAndLocation.Request) -> Task { - Task { - async let profileImageData = self.worker?.fetchImageData(with: request.profileImageURL) - async let location: String? = self.worker?.transLocation(latitude: request.latitude, longitude: request.longitude) - let response: Models.LoadProfileImageAndLocation.Response = Models.LoadProfileImageAndLocation.Response(curCell: request.curCell, profileImageData: await profileImageData, location: await location) - await MainActor.run { - presenter?.presentLoadProfileImageAndLocation(with: response) - return true - } - return false + func loadProfileImageAndLocation(with request: PlaybackModels.LoadProfileImageAndLocation.Request) async { + async let profileImageData = self.worker?.fetchImageData(with: request.profileImageURL) + async let location: String? = self.worker?.transLocation(latitude: request.latitude, longitude: request.longitude) + let response: Models.LoadProfileImageAndLocation.Response = Models.LoadProfileImageAndLocation.Response(curCell: request.curCell, profileImageData: await profileImageData, location: await location) + await MainActor.run { + presenter?.presentLoadProfileImageAndLocation(with: response) } } } diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift index fd8eec8..e2be130 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift @@ -272,11 +272,13 @@ extension PlaybackViewController: PlaybackDisplayLogic { } } cell.addAVPlayer(url: playbackVideo.displayedPost.board.videoURL) - self.interactor?.loadProfileImageAndLocation(with: Models.LoadProfileImageAndLocation.Request( - curCell: cell, - profileImageURL: playbackVideo.displayedPost.member.profileImageURL, - latitude: playbackVideo.displayedPost.board.latitude, - longitude: playbackVideo.displayedPost.board.longitude)) + Task { + await self.interactor?.loadProfileImageAndLocation(with: Models.LoadProfileImageAndLocation.Request( + curCell: cell, + profileImageURL: playbackVideo.displayedPost.member.profileImageURL, + latitude: playbackVideo.displayedPost.board.latitude, + longitude: playbackVideo.displayedPost.board.longitude)) + } cell.delegate = self return cell } @@ -404,7 +406,9 @@ extension PlaybackViewController: UICollectionViewDelegate { let currentOffset = scrollView.contentOffset.y let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height if maximumOffset < currentOffset { - interactor?.fetchPosts() + Task { + await interactor?.fetchPosts() + } } } } From 77c62a841bd3ff39386783384113ef7d5c5270d6 Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:00:14 +0900 Subject: [PATCH 03/16] =?UTF-8?q?:recycle:=20delete=20logic=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift index 58da271..8d9ff6d 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift @@ -278,7 +278,7 @@ final class PlaybackInteractor: PlaybackBusinessLogic, PlaybackDataStore { isNeedReplace: true) playbackVideoInfos.append(playbackVideoInfos[request.indexPathRow - 1]) if let posts { - self.posts?.append((posts[2])) + self.posts?.insert(posts[request.indexPathRow - 1], at: 0) } } else { response = Models.DeletePlaybackVideo.Response( From 72e548a856195ac66dae66bc355dc766bab385e9 Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:38:26 +0900 Subject: [PATCH 04/16] :white_check_mark: PlaybackPresenter Test Update --- iOS/Layover/Layover.xcodeproj/project.pbxproj | 12 + .../Scenes/Playback/PlaybackPresenter.swift | 5 - .../Playback/PlaybackViewController.swift | 5 - .../Workers/PlaybackPresenterTests.swift | 417 ++++++++++++++++++ iOS/Layover/LayoverTests/Seeds.swift | 40 ++ 5 files changed, 469 insertions(+), 10 deletions(-) create mode 100644 iOS/Layover/LayoverTests/Mocks/Workers/PlaybackPresenterTests.swift diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index 3455019..d647afc 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -145,6 +145,7 @@ 835A61A22B068115002F22A5 /* PlaybackInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835A619C2B068115002F22A5 /* PlaybackInteractor.swift */; }; 835A61A62B0B4DDD002F22A5 /* Dashboard-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 835A61A52B0B4DDD002F22A5 /* Dashboard-Regular.ttf */; }; 835A61A92B0B5A31002F22A5 /* LoginConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835A61A82B0B5A31002F22A5 /* LoginConfigurator.swift */; }; + 8363A32D2B4C1CBB00772DDF /* PlaybackPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */; }; 836C33872B15A29600ECAFB0 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33862B15A29600ECAFB0 /* Toast.swift */; }; 836C338B2B15D22C00ECAFB0 /* PlaybackConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C338A2B15D22C00ECAFB0 /* PlaybackConfigurator.swift */; }; 836C33912B17629400ECAFB0 /* MapRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33902B17629400ECAFB0 /* MapRouter.swift */; }; @@ -402,6 +403,7 @@ 835A61A52B0B4DDD002F22A5 /* Dashboard-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Dashboard-Regular.ttf"; sourceTree = ""; }; 835A61A82B0B5A31002F22A5 /* LoginConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginConfigurator.swift; sourceTree = ""; }; 835A61AA2B0B85FD002F22A5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LaunchScreen.strings; sourceTree = ""; }; + 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackPresenterTests.swift; path = LayoverTests/Mocks/Workers/PlaybackPresenterTests.swift; sourceTree = SOURCE_ROOT; }; 836C33862B15A29600ECAFB0 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; 836C338A2B15D22C00ECAFB0 /* PlaybackConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackConfigurator.swift; sourceTree = ""; }; 836C33902B17629400ECAFB0 /* MapRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRouter.swift; sourceTree = ""; }; @@ -619,6 +621,7 @@ 194C21C72B1DF09B00C62645 /* Scenes */ = { isa = PBXGroup; children = ( + 8363A32B2B4C1C6900772DDF /* Playback */, 19AE481D2B29D02700DD4612 /* EditProfile */, 19AE48122B28C2A800DD4612 /* Setting */, 192513632B26F7BB001533FA /* TagPlayList */, @@ -869,6 +872,14 @@ path = Playback; sourceTree = ""; }; + 8363A32B2B4C1C6900772DDF /* Playback */ = { + isa = PBXGroup; + children = ( + 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */, + ); + path = Playback; + sourceTree = ""; + }; 836C33922B18436A00ECAFB0 /* Setting */ = { isa = PBXGroup; children = ( @@ -1595,6 +1606,7 @@ 192513802B277CD7001533FA /* ProfileViewControllerTests.swift in Sources */, 194C21C62B1DEE6B00C62645 /* HomePresenterTests.swift in Sources */, FC4E0C202B28B4C500152596 /* MockLocationFetcher.swift in Sources */, + 8363A32D2B4C1CBB00772DDF /* PlaybackPresenterTests.swift in Sources */, 1925137A2B273D98001533FA /* StubAuthManager.swift in Sources */, 1925136D2B26F84E001533FA /* MockTagPlayListWorker.swift in Sources */, 19AE481A2B28C2B700DD4612 /* SettingPresenterTests.swift in Sources */, diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackPresenter.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackPresenter.swift index 9a1a087..f62038d 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackPresenter.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackPresenter.swift @@ -11,7 +11,6 @@ import Foundation protocol PlaybackPresentationLogic { func presentVideoList(with response: PlaybackModels.LoadPlaybackVideoList.Response) func presentLoadFetchVideos(with response: PlaybackModels.LoadPlaybackVideoList.Response) - func presentSetCellIfInfinite(with response: PlaybackModels.SetInitialPlaybackCell.Response) func presentMoveCellNext(with response: PlaybackModels.DisplayPlaybackVideo.Response) func presentSetInitialPlaybackCell(with response: PlaybackModels.SetInitialPlaybackCell.Response) func presentMoveInitialPlaybackCell(with response: PlaybackModels.SetInitialPlaybackCell.Response) @@ -50,10 +49,6 @@ final class PlaybackPresenter: PlaybackPresentationLogic { viewController?.loadFetchVideos(viewModel: viewModel) } - func presentSetCellIfInfinite(with response: PlaybackModels.SetInitialPlaybackCell.Response) { - viewController?.displayMoveCellIfinfinite(viewModel: Models.SetInitialPlaybackCell.ViewModel(indexPathRow: response.indexPathRow)) - } - // MARK: - UseCase Set Init Playback Scene func presentSetInitialPlaybackCell(with response: PlaybackModels.SetInitialPlaybackCell.Response) { diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift index e2be130..7637f57 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift @@ -19,7 +19,6 @@ protocol PlaybackViewControllerDelegate: AnyObject { protocol PlaybackDisplayLogic: AnyObject { func displayVideoList(viewModel: PlaybackModels.LoadPlaybackVideoList.ViewModel) func loadFetchVideos(viewModel: PlaybackModels.LoadPlaybackVideoList.ViewModel) - func displayMoveCellIfinfinite(viewModel: PlaybackModels.SetInitialPlaybackCell.ViewModel) func stopPrevPlayerAndPlayCurPlayer(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel) func setInitialPlaybackCell(viewModel: PlaybackModels.SetInitialPlaybackCell.ViewModel) func moveInitialPlaybackCell(viewModel: PlaybackModels.SetInitialPlaybackCell.ViewModel) @@ -215,10 +214,6 @@ extension PlaybackViewController: PlaybackDisplayLogic { dataSource?.apply(currentSnapshot, animatingDifferences: true) } - func displayMoveCellIfinfinite(viewModel: Models.SetInitialPlaybackCell.ViewModel) { - playbackCollectionView.setContentOffset(.init(x: playbackCollectionView.contentOffset.x, y: playbackCollectionView.bounds.height * CGFloat(viewModel.indexPathRow)), animated: false) - } - func stopPrevPlayerAndPlayCurPlayer(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel) { guard let tabBarHeight: CGFloat = self.tabBarController?.tabBar.frame.height else { return } if let previousCell = viewModel.previousCell { diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackPresenterTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackPresenterTests.swift new file mode 100644 index 0000000..51111fa --- /dev/null +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackPresenterTests.swift @@ -0,0 +1,417 @@ +// +// PlaybackPresenterTests.swift +// LayoverTests +// +// Created by 황지웅 on 12/12/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +@testable import Layover +import XCTest + +final class PlaybackPresenterTests: XCTestCase { + // MARK: Subject under test + + var sut: PlaybackPresenter! + + typealias Models = PlaybackModels + + // MARK: - Test lifecycle + + override func setUp() { + super.setUp() + setupPlaybackPresenter() + + } + + override func tearDown() { + super.tearDown() + } + + // MARK: - Test setup + + func setupPlaybackPresenter() { + sut = PlaybackPresenter() + } + + // MARK: - Test doubles + + final class PlaybackDisplayLogicSpy: PlaybackDisplayLogic { + var displayVideoListDidCalled = false + var displayVideoListViewModel: Models.LoadPlaybackVideoList.ViewModel! + + var loadFetchVideosDidCalled = false + var loadFetchVideosViewModel: Models.LoadPlaybackVideoList.ViewModel! + + var setInitialPlaybackCellDidCalled = false + var setInitialPlaybackCellViewModel: Models.SetInitialPlaybackCell.ViewModel! + + var moveInitialPlaybackCellDidCalled = false + var moveInitialPlaybackCellViewModel: Models.SetInitialPlaybackCell.ViewModel! + + var stopPrevPlayerAndPlayCurPlayerDidCalled = false + var stopPrevPlayerAndPlayCurPlayerViewModel: Models.DisplayPlaybackVideo.ViewModel! + + var showPlayerSliderDidCalled = false + var showPlayerSliderViewModel: Models.DisplayPlaybackVideo.ViewModel! + + var teleportPlaybackCellDidCalled = false + var teleportPlaybackCellViewModel: Models.DisplayPlaybackVideo.ViewModel! + + var leavePlaybackViewDidCalled = false + var leavePlaybackViewViewModel: Models.DisplayPlaybackVideo.ViewModel! + + var resetVideoDidCalled = false + var resetVideoViewModel: Models.DisplayPlaybackVideo.ViewModel! + + var configureDataSourceDidCalled = false + var configureDataSourceViewModel: Models.ConfigurePlaybackCell.ViewModel! + + var setSeemoreButtonDidCalled = false + var setSeemoreButtonViewModel: Models.SetSeemoreButton.ViewModel! + + var deleteVideoDidCalled = false + var deleteVideoViewModel: Models.DeletePlaybackVideo.ViewModel! + + var routeToProfileDidCalled = false + + var routeToTagPlayDidCalled = false + + var setProfileImageAndLocationDidCalled = false + var setProfileImageAndLocationViewModel: Models.LoadProfileImageAndLocation.ViewModel! + + var seekVideoDidCalled = false + var seekVideoViewModel: Models.SeekVideo.ViewModel! + + var reportVideoDidCalled = false + + func displayVideoList(viewModel: Layover.PlaybackModels.LoadPlaybackVideoList.ViewModel) { + displayVideoListDidCalled = true + displayVideoListViewModel = viewModel + } + + func loadFetchVideos(viewModel: Layover.PlaybackModels.LoadPlaybackVideoList.ViewModel) { + loadFetchVideosDidCalled = true + loadFetchVideosViewModel = viewModel + } + + func setInitialPlaybackCell(viewModel: Layover.PlaybackModels.SetInitialPlaybackCell.ViewModel) { + setInitialPlaybackCellDidCalled = true + setInitialPlaybackCellViewModel = viewModel + } + + func moveInitialPlaybackCell(viewModel: Layover.PlaybackModels.SetInitialPlaybackCell.ViewModel) { + moveInitialPlaybackCellDidCalled = true + moveInitialPlaybackCellViewModel = viewModel + } + + func stopPrevPlayerAndPlayCurPlayer(viewModel: Layover.PlaybackModels.DisplayPlaybackVideo.ViewModel) { + stopPrevPlayerAndPlayCurPlayerDidCalled = true + stopPrevPlayerAndPlayCurPlayerViewModel = viewModel + } + + func showPlayerSlider(viewModel: Layover.PlaybackModels.DisplayPlaybackVideo.ViewModel) { + showPlayerSliderDidCalled = true + showPlayerSliderViewModel = viewModel + } + + func teleportPlaybackCell(viewModel: Layover.PlaybackModels.DisplayPlaybackVideo.ViewModel) { + teleportPlaybackCellDidCalled = true + teleportPlaybackCellViewModel = viewModel + } + + func leavePlaybackView(viewModel: Layover.PlaybackModels.DisplayPlaybackVideo.ViewModel) { + leavePlaybackViewDidCalled = true + leavePlaybackViewViewModel = viewModel + } + + func resetVideo(viewModel: Layover.PlaybackModels.DisplayPlaybackVideo.ViewModel) { + resetVideoDidCalled = true + resetVideoViewModel = viewModel + } + + func configureDataSource(viewModel: Layover.PlaybackModels.ConfigurePlaybackCell.ViewModel) { + configureDataSourceDidCalled = true + configureDataSourceViewModel = viewModel + } + + func setSeemoreButton(viewModel: Layover.PlaybackModels.SetSeemoreButton.ViewModel) { + setSeemoreButtonDidCalled = true + setSeemoreButtonViewModel = viewModel + } + + func deleteVideo(viewModel: Layover.PlaybackModels.DeletePlaybackVideo.ViewModel) { + deleteVideoDidCalled = true + deleteVideoViewModel = viewModel + } + + func routeToProfile() { + routeToProfileDidCalled = true + } + + func routeToTagPlay() { + routeToTagPlayDidCalled = true + } + + func setProfileImageAndLocation(viewModel: Layover.PlaybackModels.LoadProfileImageAndLocation.ViewModel) { + setProfileImageAndLocationDidCalled = true + setProfileImageAndLocationViewModel = viewModel + } + + func seekVideo(viewModel: Layover.PlaybackModels.SeekVideo.ViewModel) { + seekVideoDidCalled = true + seekVideoViewModel = viewModel + } + + func reportVideo() { + reportVideoDidCalled = true + } + } + + // MARK: - Tests + + func test_presentVideoList를_호출하면_displayVideoList를_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.LoadPlaybackVideoList.Response = Models.LoadPlaybackVideoList.Response(videos: Seeds.PlaybackVideos.videos) + + // act + sut.presentVideoList(with: response) + + // assert + XCTAssertTrue(spy.displayVideoListDidCalled, "presentProfile은 displayVideoList를 호출했다") + // videos는 Hashable + XCTAssertEqual(spy.displayVideoListViewModel.videos.first!, Seeds.PlaybackVideos.videos.first!) + XCTAssertEqual(spy.displayVideoListViewModel.videos.last!, Seeds.PlaybackVideos.videos.last!) + XCTAssertEqual(spy.displayVideoListViewModel.videos.count, 2) + } + + func test_presentLoadFetchVideos를_호출하면_loadFetchVideos를_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.LoadPlaybackVideoList.Response = Models.LoadPlaybackVideoList.Response(videos: Seeds.PlaybackVideos.videos) + // act + sut.presentLoadFetchVideos(with: response) + + // assert + XCTAssertTrue(spy.loadFetchVideosDidCalled, "presentLoadFetchVideos는 loadFetchVideos를 호출했다") + XCTAssertEqual(spy.loadFetchVideosViewModel.videos.first!, Seeds.PlaybackVideos.videos.first!) + XCTAssertEqual(spy.loadFetchVideosViewModel.videos.last!, Seeds.PlaybackVideos.videos.last!) + XCTAssertEqual(spy.loadFetchVideosViewModel.videos.count, 2) + } + + func test_presentSetInitialPlaybackCell을_호출하면_setInitialPlaybackCell을_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.SetInitialPlaybackCell.Response = Models.SetInitialPlaybackCell.Response(indexPathRow: 1) + // act + sut.presentSetInitialPlaybackCell(with: response) + + // assert + XCTAssertTrue(spy.setInitialPlaybackCellDidCalled, "presentSetInitialPlaybackCell은 setInitialPlaybackCell을 호출했다") + XCTAssertEqual(spy.setInitialPlaybackCellViewModel.indexPathRow, 1) + } + + func test_presentMoveInitialPlaybackCell을_호출하면_moveInitialPlaybackCell을_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.SetInitialPlaybackCell.Response = Models.SetInitialPlaybackCell.Response(indexPathRow: 1) + // act + sut.presentMoveInitialPlaybackCell(with: response) + + // assert + XCTAssertTrue(spy.moveInitialPlaybackCellDidCalled, "presentMoveInitialPlaybackCell은 moveInitialPlaybackCell을 호출했다") + XCTAssertEqual(spy.moveInitialPlaybackCellViewModel.indexPathRow, 1) + } + + func test_presentMoveCellNext를_호출하면_stopPrevPlayerAndPlayCurPlayer를_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.DisplayPlaybackVideo.Response = Models.DisplayPlaybackVideo.Response(previousCell: Seeds.PlaybackVideo.previousCell, currentCell: Seeds.PlaybackVideo.currentCell) + // act + sut.presentMoveCellNext(with: response) + + // assert + XCTAssertTrue(spy.stopPrevPlayerAndPlayCurPlayerDidCalled, "presentMoveCellNext는 stopPrevPlayerAndPlayCurPlayerDidCalled를 호출했다") + // 이전 PlaybackCell과 현재 PlaybackCell은 달라야함 + XCTAssertNotEqual(spy.stopPrevPlayerAndPlayCurPlayerViewModel.previousCell, spy.stopPrevPlayerAndPlayCurPlayerViewModel.currentCell) + XCTAssertEqual(spy.stopPrevPlayerAndPlayCurPlayerViewModel.previousCell, Seeds.PlaybackVideo.previousCell) + XCTAssertEqual(spy.stopPrevPlayerAndPlayCurPlayerViewModel.currentCell, Seeds.PlaybackVideo.currentCell) + } + + func test_presentPlayInitialPlaybackCell을_호출하면_stopPrevPlayerAndPlayCurPlayer를_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.DisplayPlaybackVideo.Response = Models.DisplayPlaybackVideo.Response(previousCell: nil, currentCell: Seeds.PlaybackVideo.currentCell) + // act + sut.presentPlayInitialPlaybackCell(with: response) + + // assert + XCTAssertTrue(spy.stopPrevPlayerAndPlayCurPlayerDidCalled, "presentPlayInitialPlaybackCell은 stopPrevPlayerAndPlayCurPlayerDidCalled를 호출했다") + XCTAssertNotEqual(spy.stopPrevPlayerAndPlayCurPlayerViewModel.previousCell, spy.stopPrevPlayerAndPlayCurPlayerViewModel.currentCell) + XCTAssertEqual(spy.stopPrevPlayerAndPlayCurPlayerViewModel.previousCell, nil) + XCTAssertEqual(spy.stopPrevPlayerAndPlayCurPlayerViewModel.currentCell, Seeds.PlaybackVideo.currentCell) + } + + func test_presentShowPlayerSlider를_호출하면_showPlayerSlider를_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.DisplayPlaybackVideo.Response = Models.DisplayPlaybackVideo.Response(previousCell: nil, currentCell: Seeds.PlaybackVideo.currentCell) + // act + sut.presentShowPlayerSlider(with: response) + + // assert + XCTAssertTrue(spy.showPlayerSliderDidCalled, "presentShowPlayerSlider은 showPlayerSlider를 호출했다") + XCTAssertNotEqual(spy.showPlayerSliderViewModel.previousCell, spy.showPlayerSliderViewModel.currentCell) + XCTAssertEqual(spy.showPlayerSliderViewModel.previousCell, nil) + XCTAssertEqual(spy.showPlayerSliderViewModel.currentCell, Seeds.PlaybackVideo.currentCell) + } + + func test_presentTeleportCell을_호출하면_teleportPlaybackCell을_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.DisplayPlaybackVideo.Response = Models.DisplayPlaybackVideo.Response(indexPathRow: 3, previousCell: nil, currentCell: nil) + // act + sut.presentTeleportCell(with: response) + + // assert + XCTAssertTrue(spy.teleportPlaybackCellDidCalled, "presentTeleportCell은 teleportPlaybackCell을 호출하지 못했습니다") + XCTAssertEqual(spy.teleportPlaybackCellViewModel.indexPathRow, 3) + } + + func test_presentLeavePlaybackView를_호출하면_leavePlaybackView를_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.DisplayPlaybackVideo.Response = Models.DisplayPlaybackVideo.Response(previousCell: Seeds.PlaybackVideo.previousCell, currentCell: nil) + // act + sut.presentLeavePlaybackView(with: response) + + // assert + XCTAssertTrue(spy.leavePlaybackViewDidCalled, "presentLeavePlaybackView는 teleportPlaybackCell을 호출하지 못했습니다") + XCTAssertNotEqual(spy.leavePlaybackViewViewModel.previousCell, spy.leavePlaybackViewViewModel.currentCell) + XCTAssertEqual(spy.leavePlaybackViewViewModel.previousCell, Seeds.PlaybackVideo.previousCell) + XCTAssertEqual(spy.leavePlaybackViewViewModel.currentCell, nil) + } + + func test_presentResetPlaybackCell을_호출하면_resetVideo를_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.DisplayPlaybackVideo.Response = Models.DisplayPlaybackVideo.Response(previousCell: nil, currentCell: Seeds.PlaybackVideo.currentCell) + // act + sut.presentResetPlaybackCell(with: response) + + // assert + XCTAssertTrue(spy.resetVideoDidCalled, "presentResetPlaybackCell은 resetVideo를 호출하지 못했습니다") + XCTAssertNotEqual(spy.resetVideoViewModel.previousCell, spy.resetVideoViewModel.currentCell) + XCTAssertEqual(spy.resetVideoViewModel.previousCell, nil) + XCTAssertEqual(spy.resetVideoViewModel.currentCell, Seeds.PlaybackVideo.currentCell) + } + + func test_presentConfigureCell을_호출하면_configureDataSource를_호출하고_올바른_데이터를_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.ConfigurePlaybackCell.Response = Models.ConfigurePlaybackCell.Response(teleportIndex: 3) + // act + sut.presentConfigureCell(with: response) + + // assert + XCTAssertTrue(spy.configureDataSourceDidCalled, "presentConfigureDataSource는 configureDataSource를 호출하지 못했습니다") + XCTAssertEqual(spy.configureDataSourceViewModel.teleportIndex, 3) + } + + func test_presentSetSeemoreButton을_호출하면_setSeemoreButton을_호출하고_올바른_데이터를_전달한다_delete() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.SetSeemoreButton.Response = Models.SetSeemoreButton.Response(buttonType: .delete, indexPathRow: 0) + // act + sut.presentSetSeemoreButton(with: response) + + // assert + XCTAssertTrue(spy.setSeemoreButtonDidCalled, "presentSetSeemoreButton은 setSeemoreButton을 호출하지 못했습니다") + XCTAssertEqual(spy.setSeemoreButtonViewModel.buttonType, .delete) + } + + func test_presentSetSeemoreButton을_호출하면_setSeemoreButton을_호출하고_올바른_데이터를_전달한다_report() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.SetSeemoreButton.Response = Models.SetSeemoreButton.Response(buttonType: .report, indexPathRow: 0) + // act + sut.presentSetSeemoreButton(with: response) + + // assert + XCTAssertTrue(spy.setSeemoreButtonDidCalled, "presentSetSeemoreButton은 setSeemoreButton을 호출하지 못했습니다") + XCTAssertEqual(spy.setSeemoreButtonViewModel.buttonType, .report) + } + + func test_presentProfile을_호출하면_routeToProfile을_호출한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + // act + sut.presentProfile() + + // assert + XCTAssertTrue(spy.routeToProfileDidCalled, "presentProfile은 routeToProfile을 호출하지 못했습니다") + } + + func test_presentTagPlay을_호출하면_routeToTagPlay을_호출한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + // act + sut.presentTagPlay() + + // assert + XCTAssertTrue(spy.routeToTagPlayDidCalled, "presentTagPlay는 routeToTagPlay를 호출하지 못했습니다") + } + + func test_presentLoadProfileImageAndLocation을_호출하면_setProfileImageAndLocation을_호출한다_nil일_경우_지역으로_이름_모를_곳을_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.LoadProfileImageAndLocation.Response = Models.LoadProfileImageAndLocation.Response(curCell: Seeds.PlaybackVideo.currentCell, profileImageData: Seeds.PlaybackVideo.profileImageData, location: nil) + // act + sut.presentLoadProfileImageAndLocation(with: response) + + // assert + XCTAssertTrue(spy.setProfileImageAndLocationDidCalled, "presentLoadProfileImageAndLocation은 setProfileImageAndLocation를 호출하지 못했습니다") + XCTAssertEqual(spy.setProfileImageAndLocationViewModel.curCell, Seeds.PlaybackVideo.currentCell) + XCTAssertEqual(spy.setProfileImageAndLocationViewModel.profileImageData, Seeds.PlaybackVideo.profileImageData) + XCTAssertEqual(spy.setProfileImageAndLocationViewModel.location, "이름 모를 곳") + } + + func test_presentLoadProfileImageAndLocation을_호출하면_setProfileImageAndLocation을_호출한다_지역이_존재할_경우_지역으로_지역이름을_전달한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.LoadProfileImageAndLocation.Response = Models.LoadProfileImageAndLocation.Response(curCell: Seeds.PlaybackVideo.currentCell, profileImageData: Seeds.PlaybackVideo.profileImageData, location: "서울특별시") + // act + sut.presentLoadProfileImageAndLocation(with: response) + + // assert + XCTAssertTrue(spy.setProfileImageAndLocationDidCalled, "presentLoadProfileImageAndLocation은 setProfileImageAndLocation를 호출하지 못했습니다") + XCTAssertEqual(spy.setProfileImageAndLocationViewModel.curCell, Seeds.PlaybackVideo.currentCell) + XCTAssertEqual(spy.setProfileImageAndLocationViewModel.profileImageData, Seeds.PlaybackVideo.profileImageData) + XCTAssertEqual(spy.setProfileImageAndLocationViewModel.location, "서울특별시") + } + + func test_presentSeekVideo를_호출하면_seekVideo를_호출한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + let response: Models.SeekVideo.Response = Models.SeekVideo.Response(willMoveLocation: 1, currentCell: Seeds.PlaybackVideo.currentCell) + // act + sut.presentSeekVideo(with: response) + + // assert + XCTAssertTrue(spy.seekVideoDidCalled, "presentSeekVideo를 호출하지 못했습니다.") + XCTAssertEqual(spy.seekVideoViewModel.willMoveLocation, 1) + XCTAssertEqual(spy.seekVideoViewModel.currentCell, Seeds.PlaybackVideo.currentCell) + } + + func test_presentReportVideo를_호출하면_reportVideo를_호출한다() { + let spy = PlaybackDisplayLogicSpy() + sut.viewController = spy + + // act + sut.presentReportVideo() + + // assert + XCTAssertTrue(spy.reportVideoDidCalled, "presentReportVideo를 호출하지 못했습니다") + } +} diff --git a/iOS/Layover/LayoverTests/Seeds.swift b/iOS/Layover/LayoverTests/Seeds.swift index 294d99c..ec3ed8b 100644 --- a/iOS/Layover/LayoverTests/Seeds.swift +++ b/iOS/Layover/LayoverTests/Seeds.swift @@ -83,4 +83,44 @@ class Seeds { introduce: "안녕하세요, 아이브의 안유진입니다.", profileImageURL: URL(string: "https://cdn.footballist.co.kr/news/photo/202307/170226_100422_1733.jpg")!) } + + struct PlaybackVideos { + static let videos: [PlaybackModels.PlaybackVideo] = + [PlaybackModels.PlaybackVideo( + displayedPost: PlaybackModels.DisplayedPost( + member: PlaybackModels.Member( + memberID: 1, + username: "안유진", + profileImageURL: URL(string: "https://cdn.footballist.co.kr/news/photo/202307/170226_100422_1733.jpg")), + board: PlaybackModels.Board( + boardID: 1, + title: "최강 아이돌", + description: "게시글 설명", + videoURL: URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8")!, + latitude: 127.060123123, + longitude: 37.0532156213), + tags: ["아이브", "yujin"])), + PlaybackModels.PlaybackVideo( + displayedPost: PlaybackModels.DisplayedPost( + member: PlaybackModels.Member( + memberID: 2, + username: "장원영", + profileImageURL: URL(string: "https://www.fnnews.com/resource/media/image/2023/10/17/202310171126334525_l.jpg")), + board: PlaybackModels.Board( + boardID: 2, + title: "아이브의 멤버", + description: "게시글 설명설명설명", + videoURL: URL(string: "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8")!, + latitude: 100.060123123, + longitude: 35.001828282), + tags: ["Ive", "wonyoung"])) + ] + } + + enum PlaybackVideo { + static let previousCell: PlaybackCell = PlaybackCell() + static let currentCell: PlaybackCell = PlaybackCell() + static let profileImageData: Data? = try? Data(contentsOf: Bundle(for: Seeds.self).url(forResource: "sample", withExtension: "jpeg")!) + static let url: URL? = URL(string: "https://www.fnnews.com/resource/media/image/2023/10/17/202310171126334525_l.jpg") + } } From 166e75680d655f153bedc13d136f63c308cff18c Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Tue, 9 Jan 2024 00:39:21 +0900 Subject: [PATCH 05/16] =?UTF-8?q?:white=5Fcheck=5Fmark:=20PlaybackInteract?= =?UTF-8?q?or=20Test=201=EC=B0=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover.xcodeproj/project.pbxproj | 4 + .../Scenes/Playback/PlaybackInteractor.swift | 2 +- .../Workers/Mocks/MockPlaybackWorker.swift | 13 +- .../Workers/PlaybackInteractorTests.swift | 652 ++++++++++++++++++ 4 files changed, 666 insertions(+), 5 deletions(-) create mode 100644 iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index d647afc..e31f751 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -146,6 +146,7 @@ 835A61A62B0B4DDD002F22A5 /* Dashboard-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 835A61A52B0B4DDD002F22A5 /* Dashboard-Regular.ttf */; }; 835A61A92B0B5A31002F22A5 /* LoginConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835A61A82B0B5A31002F22A5 /* LoginConfigurator.swift */; }; 8363A32D2B4C1CBB00772DDF /* PlaybackPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */; }; + 8363A32F2B4C329100772DDF /* PlaybackInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8363A32E2B4C329100772DDF /* PlaybackInteractorTests.swift */; }; 836C33872B15A29600ECAFB0 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33862B15A29600ECAFB0 /* Toast.swift */; }; 836C338B2B15D22C00ECAFB0 /* PlaybackConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C338A2B15D22C00ECAFB0 /* PlaybackConfigurator.swift */; }; 836C33912B17629400ECAFB0 /* MapRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33902B17629400ECAFB0 /* MapRouter.swift */; }; @@ -404,6 +405,7 @@ 835A61A82B0B5A31002F22A5 /* LoginConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginConfigurator.swift; sourceTree = ""; }; 835A61AA2B0B85FD002F22A5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LaunchScreen.strings; sourceTree = ""; }; 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackPresenterTests.swift; path = LayoverTests/Mocks/Workers/PlaybackPresenterTests.swift; sourceTree = SOURCE_ROOT; }; + 8363A32E2B4C329100772DDF /* PlaybackInteractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackInteractorTests.swift; path = LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift; sourceTree = SOURCE_ROOT; }; 836C33862B15A29600ECAFB0 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; 836C338A2B15D22C00ECAFB0 /* PlaybackConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackConfigurator.swift; sourceTree = ""; }; 836C33902B17629400ECAFB0 /* MapRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRouter.swift; sourceTree = ""; }; @@ -876,6 +878,7 @@ isa = PBXGroup; children = ( 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */, + 8363A32E2B4C329100772DDF /* PlaybackInteractorTests.swift */, ); path = Playback; sourceTree = ""; @@ -1612,6 +1615,7 @@ 19AE481A2B28C2B700DD4612 /* SettingPresenterTests.swift in Sources */, 19AE48172B28C2B700DD4612 /* SettingViewControllerTests.swift in Sources */, 194C21C32B1DEE6B00C62645 /* HomeViewControllerTests.swift in Sources */, + 8363A32F2B4C329100772DDF /* PlaybackInteractorTests.swift in Sources */, 192513692B26F7CE001533FA /* TagPlayListInteractorTests.swift in Sources */, 19AE48232B29D03D00DD4612 /* EditProfileInteractorTests.swift in Sources */, 194C21CC2B1DF39200C62645 /* MockHomeWorker.swift in Sources */, diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift index 8d9ff6d..ef93b5a 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift @@ -77,7 +77,7 @@ final class PlaybackInteractor: PlaybackBusinessLogic, PlaybackDataStore { private var currentPage: Int = 1 - private var playbackVideoInfos: [Models.PlaybackInfo] = [] + var playbackVideoInfos: [Models.PlaybackInfo] = [] // MARK: - UseCase Load Video List diff --git a/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift b/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift index 0a418b5..58d4dd4 100644 --- a/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift +++ b/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift @@ -27,10 +27,15 @@ final class MockPlaybackWorker: PlaybackWorkerProtocol { } func makeInfiniteScroll(posts: [Post]) -> [Post] { - guard let tempLastVideo: Post = posts.last, - let tempFirstVideo: Post = posts.first - else { return posts } - var tempVideos: [Post] = posts + var tempVideos: [Post] = [] + for post in posts { + if post.board.videoURL != nil, post.board.status == .complete { + tempVideos.append(post) + } + } + guard let tempLastVideo: Post = tempVideos.last, + let tempFirstVideo: Post = tempVideos.first + else { return tempVideos } tempVideos.insert(tempLastVideo, at: 0) tempVideos.append(tempFirstVideo) return tempVideos diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift new file mode 100644 index 0000000..1483b34 --- /dev/null +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift @@ -0,0 +1,652 @@ +// +// PlaybackInteractorTests.swift +// LayoverTests +// +// Created by 황지웅 on 12/12/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +@testable import Layover +import XCTest + +final class PlaybackInteractorTests: XCTestCase { + // MARK: Subject under test + + var sut: PlaybackInteractor! + + // MARK: Properties + + typealias Models = PlaybackModels + + // MARK: - Test lifecycle + + override func setUp() { + super.setUp() + setupPlaybackInteracotr() + } + + override func tearDown() { + super.tearDown() + } + + // MARK: - Test setup + + func setupPlaybackInteracotr() { + sut = PlaybackInteractor() + sut.worker = MockPlaybackWorker() + } + + // MARK: - Test doubles + + final class PlaybackPresentationLogicSpy: PlaybackPresentationLogic { + var presentVideoListDidCalled = false + var presentVideoListResponse: Models.LoadPlaybackVideoList.Response! + + var presentLoadFetchVideosDidCalled = false + var presentLoadFetchVideosResponse: Models.LoadPlaybackVideoList.Response! + + var presentSetInitialPlaybackCellDidCalled = false + var presentSetInitialPlaybackCellResponse: Models.SetInitialPlaybackCell.Response! + + var presentMoveInitialPlaybackCellDidCalled = false + var presentMoveInitialPlaybackCellResponse: Models.SetInitialPlaybackCell.Response! + + var presentMoveCellNextDidCalled = false + var presentMoveCellNextResponse: Models.DisplayPlaybackVideo.Response! + + var presentPlayInitialPlaybackCellDidCalled = false + var presentPlayInitialPlaybackCellResponse: Models.DisplayPlaybackVideo.Response! + + var presentShowPlayerSliderDidCalled = false + var presentShowPlayerSliderResponse: Models.DisplayPlaybackVideo.Response! + + var presentTeleportCellDidCalled = false + var presentTeleportCellResponse: Models.DisplayPlaybackVideo.Response! + + var presentLeavePlaybackViewDidCalled = false + var presentLeavePlaybackViewResponse: Models.DisplayPlaybackVideo.Response! + + var presentResetPlaybackCellDidCalled = false + var presentResetPlaybackCellResponse: Models.DisplayPlaybackVideo.Response! + + var presentConfigureCellDidCalled = false + var presentConfigureCellResponse: Models.ConfigurePlaybackCell.Response! + + var presentSetSeemoreButtonDidCalled = false + var presentSetSeemoreButtonResponse: Models.SetSeemoreButton.Response! + + var presentDeleteVideoDidCalled = false + var presentDeleteVideoResponse: Models.DeletePlaybackVideo.Response! + + var presentProfileDidCalled = false + + var presentTagPlayDidCalled = false + + var presentLoadProfileImageAndLocationDidCalled = false + var presentLoadProfileImageAndLocationResponse: Models.LoadProfileImageAndLocation.Response! + + var presentSeekVideoDidCalled = false + var presentSeekVideoResponse: Models.SeekVideo.Response! + + var presentReportVideoDidCalled = false + + func presentVideoList(with response: Layover.PlaybackModels.LoadPlaybackVideoList.Response) { + presentVideoListDidCalled = true + presentVideoListResponse = response + } + + func presentLoadFetchVideos(with response: Layover.PlaybackModels.LoadPlaybackVideoList.Response) { + presentLoadFetchVideosDidCalled = true + presentLoadFetchVideosResponse = response + } + + func presentSetInitialPlaybackCell(with response: Layover.PlaybackModels.SetInitialPlaybackCell.Response) { + presentSetInitialPlaybackCellDidCalled = true + presentSetInitialPlaybackCellResponse = response + } + + func presentMoveInitialPlaybackCell(with response: Layover.PlaybackModels.SetInitialPlaybackCell.Response) { + presentMoveInitialPlaybackCellDidCalled = true + presentMoveInitialPlaybackCellResponse = response + } + + func presentMoveCellNext(with response: Layover.PlaybackModels.DisplayPlaybackVideo.Response) { + presentMoveCellNextDidCalled = true + presentMoveCellNextResponse = response + } + + func presentPlayInitialPlaybackCell(with response: Layover.PlaybackModels.DisplayPlaybackVideo.Response) { + presentPlayInitialPlaybackCellDidCalled = true + presentPlayInitialPlaybackCellResponse = response + } + + func presentShowPlayerSlider(with response: Layover.PlaybackModels.DisplayPlaybackVideo.Response) { + presentShowPlayerSliderDidCalled = true + presentShowPlayerSliderResponse = response + } + + func presentTeleportCell(with response: Layover.PlaybackModels.DisplayPlaybackVideo.Response) { + presentTeleportCellDidCalled = true + presentTeleportCellResponse = response + } + + func presentLeavePlaybackView(with response: Layover.PlaybackModels.DisplayPlaybackVideo.Response) { + presentLeavePlaybackViewDidCalled = true + presentLeavePlaybackViewResponse = response + } + + func presentResetPlaybackCell(with response: Layover.PlaybackModels.DisplayPlaybackVideo.Response) { + presentResetPlaybackCellDidCalled = true + presentResetPlaybackCellResponse = response + } + + func presentConfigureCell(with response: Layover.PlaybackModels.ConfigurePlaybackCell.Response) { + presentConfigureCellDidCalled = true + presentConfigureCellResponse = response + } + + func presentSetSeemoreButton(with response: Layover.PlaybackModels.SetSeemoreButton.Response) { + presentSetSeemoreButtonDidCalled = true + presentSetSeemoreButtonResponse = response + } + + func presentDeleteVideo(with response: Layover.PlaybackModels.DeletePlaybackVideo.Response) { + presentDeleteVideoDidCalled = true + presentDeleteVideoResponse = response + } + + func presentProfile() { + presentProfileDidCalled = true + } + + func presentTagPlay() { + presentTagPlayDidCalled = true + } + + func presentLoadProfileImageAndLocation(with response: Layover.PlaybackModels.LoadProfileImageAndLocation.Response) { + presentLoadProfileImageAndLocationDidCalled = true + presentLoadProfileImageAndLocationResponse = response + } + + func presentSeekVideo(with response: Layover.PlaybackModels.SeekVideo.Response) { + presentSeekVideoDidCalled = true + presentSeekVideoResponse = response + } + + func presentReportVideo() { + presentReportVideoDidCalled = true + } + } + + // MARK: - Tests + + func test_parentView가_map일_때_displayVideoList를_호출하면_presentVideoList를_호출하고_올바른_데이터를_전달한다() async { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.presenter = spy + sut.parentView = .map + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.videoURLNilPost] + + // Act + await sut.displayVideoList() + + // Assert + XCTAssertTrue(spy.presentVideoListDidCalled, "displayVideoList는 presentVideoList를 호출하지 않았습니다") + // map이므로 2 + 더미셀 2 = 4 + // videoURL이 nil이면 거름 + print(spy.presentVideoListResponse.videos) + XCTAssertEqual(spy.presentVideoListResponse.videos.count, 4) + XCTAssertEqual(spy.presentVideoListResponse.videos[0].displayedPost, spy.presentVideoListResponse.videos[2].displayedPost) + XCTAssertEqual(spy.presentVideoListResponse.videos[1].displayedPost, spy.presentVideoListResponse.videos[1].displayedPost) + XCTAssertEqual(spy.presentVideoListResponse.videos.first!.displayedPost, Seeds.PlaybackVideos.videos.last!.displayedPost) + XCTAssertEqual(spy.presentVideoListResponse.videos.last!.displayedPost, Seeds.PlaybackVideos.videos.first!.displayedPost) + } + + func test_displayVideoList를_호출하면_presentVideoList를_호출하고_올바른_데이터를_전달한다_parentView가_map이_아닐때() async { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.presenter = spy + sut.parentView = .home + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.videoURLNilPost] + + // Act + await sut.displayVideoList() + + // Assert + XCTAssertTrue(spy.presentVideoListDidCalled, "displayVideoList는 presentVideoList를 호출하지 않았습니다") + // map이므로 2 + 더미셀 2 = 4 + // videoURL이 nil이면 거름 + XCTAssertEqual(spy.presentVideoListResponse.videos.count, 2) + XCTAssertEqual(spy.presentVideoListResponse.videos.first!.displayedPost, Seeds.PlaybackVideos.videos.first!.displayedPost) + XCTAssertEqual(spy.presentVideoListResponse.videos.last!.displayedPost, Seeds.PlaybackVideos.videos.last!.displayedPost) + } + + func test_moveInitialPlaybackCell을_호출하면_presentMoveInitialPlaybackCell을_호출하고_올바른_데이터를_전달한다_parentView가_map일_때() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.parentView = .map + sut.index = 2 + sut.presenter = spy + + // Act + sut.moveInitialPlaybackCell() + + // Assert + XCTAssertTrue(spy.presentMoveInitialPlaybackCellDidCalled, "moveInitialPlaybackCell은 moveInitialPlaybackCell을 호출하지 않았습니다") + XCTAssertEqual(spy.presentMoveInitialPlaybackCellResponse.indexPathRow, 3) + } + + func test_moveInitialPlaybackCell을_호출하면_presentMoveInitialPlaybackCell을_호출하고_올바른_데이터를_전달한다_parentView가_map이_아닐때() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.parentView = .home + sut.index = 2 + sut.presenter = spy + + // Act + sut.moveInitialPlaybackCell() + + // Assert + XCTAssertTrue(spy.presentMoveInitialPlaybackCellDidCalled, "moveInitialPlaybackCell은 moveInitialPlaybackCell을 호출하지 않았습니다") + XCTAssertEqual(spy.presentMoveInitialPlaybackCellResponse.indexPathRow, 2) + } + + func test_setInitialPlaybackCell을_호출하면_presentSetInitialPlaybackCell을_호출하고_올바른_데이터를_전달한다_parentView가_map일_때() { + // Arraynge + let spy = PlaybackPresentationLogicSpy() + sut.parentView = .map + sut.index = 2 + sut.presenter = spy + + // Act + sut.setInitialPlaybackCell() + + // Assert + XCTAssertTrue(spy.presentSetInitialPlaybackCellDidCalled, "setInitialPlaybackCell은 presentSetInitialPlaybackCell을 호출하지 않았습니다.") + XCTAssertEqual(spy.presentSetInitialPlaybackCellResponse.indexPathRow, 3) + } + + func test_setInitialPlaybackCell을_호출하면_presentSetInitialPlaybackCell을_호출하고_올바른_데이터를_전달한다_parentView가_map이_아닐때() { + // Arraynge + let spy = PlaybackPresentationLogicSpy() + sut.parentView = .map + sut.index = 2 + sut.presenter = spy + + // Act + sut.setInitialPlaybackCell() + + // Assert + XCTAssertTrue(spy.presentSetInitialPlaybackCellDidCalled, "setInitialPlaybackCell은 presentSetInitialPlaybackCell을 호출하지 않았습니다") + XCTAssertEqual(spy.presentSetInitialPlaybackCellResponse.indexPathRow, 3) + } + + func test_playInitialPlaybackCell을_호출하면_presentPlayInitialPlaybackCell을_호출하고_올바른_데이터를_전달한다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: nil, currentCell: Seeds.PlaybackVideo.currentCell) + sut.presenter = spy + // Act + sut.playInitialPlaybackCell(with: request) + + // Assert + XCTAssertTrue(spy.presentPlayInitialPlaybackCellDidCalled, "playInitialPlaybackCell은 presentInitialPlaybackCell을 호출하지 않았습니다") + XCTAssertEqual(spy.presentPlayInitialPlaybackCellResponse.previousCell, nil) + XCTAssertEqual(spy.presentPlayInitialPlaybackCellResponse.currentCell, sut.previousCell) + XCTAssertEqual(spy.presentPlayInitialPlaybackCellResponse.currentCell, Seeds.PlaybackVideo.currentCell) + } + + func test_playVideo를_호출할_때_이전Cell과_현재Cell이_동일하면_presentShowPlayerSlider를_호출한다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.videoURLNilPost] + sut.previousCell = Seeds.PlaybackVideo.currentCell + let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: nil, currentCell: Seeds.PlaybackVideo.currentCell) + sut.presenter = spy + // Act + sut.playVideo(with: request) + + // Assert + XCTAssertTrue(spy.presentShowPlayerSliderDidCalled, "playVideo는 presentShowPlayerSlider를 호출하지 않았습니다") + XCTAssertEqual(spy.presentShowPlayerSliderResponse.currentCell, request.currentCell) + } + +// func test_playVideo를_호출하면_presentMoveCellNext를_호출한다_parentView가_map이고_index가_마지막을_가리킬_떄() { +// // Arrange +// let spy = PlaybackPresentationLogicSpy() +// sut.parentView = .map +// sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] +// sut.previousCell = Seeds.PlaybackVideo.previousCell +// let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: 1, currentCell: Seeds.PlaybackVideo.currentCell) +// sut.presenter = spy +// // act +// sut.playVideo(with: request) +// +// // Assert +// XCTAssertTrue(spy.presentTeleportCellDidCalled, "playVideo는 presentTeleportCell를 호출하지 않았습니다") +// XCTAssertEqual(sut.previousCell, Seeds.PlaybackVideo.previousCell) +// XCTAssertEqual(spy.presentTeleportCellResponse.previousCell, Seeds.PlaybackVideo.previousCell) +// XCTAssertEqual(spy.presentTeleportCellResponse.indexPathRow, 1) +// XCTAssertTrue(((sut.isTeleport) != nil)) +// } + +// func test_playVideo를_호출하면_presentMoveCellNext를_호출한다_parentView가_map이고_index가_처음을_가리킬_떄() { +// // Arrange +// let spy = PlaybackPresentationLogicSpy() +// sut.parentView = .map +// sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] +// sut.previousCell = Seeds.PlaybackVideo.previousCell +// let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: 1, currentCell: Seeds.PlaybackVideo.currentCell) +// sut.presenter = spy +// // act +// sut.playVideo(with: request) +// +// // Assert +// XCTAssertTrue(spy.presentTeleportCellDidCalled, "playVideo는 presentTeleportCell를 호출하지 않았습니다") +// XCTAssertEqual(spy.presentTeleportCellResponse.previousCell, Seeds.PlaybackVideo.previousCell) +// XCTAssertEqual(spy.presentTeleportCellResponse.currentCell, nil) +// XCTAssertEqual(spy.presentTeleportCellResponse.indexPathRow, 0) +// XCTAssertTrue(((sut.isTeleport) != nil)) +// } + + func test_playVideo를_호출하면_presentMoveCellNext를_호출한다_parentView가_map이고_텔레포트가_필요없는_상황일_때() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.parentView = .map + sut.previousCell = Seeds.PlaybackVideo.previousCell + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost] + sut.previousCell = Seeds.PlaybackVideo.previousCell + let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: 1, currentCell: Seeds.PlaybackVideo.currentCell) + sut.presenter = spy + + // act + sut.playVideo(with: request) + + // Assert + XCTAssertTrue(spy.presentMoveCellNextDidCalled, "playVideo는 presentMoveCellNext를 호출하지 않았습니다") + XCTAssertEqual(sut.previousCell, Seeds.PlaybackVideo.currentCell) + XCTAssertEqual(spy.presentMoveCellNextResponse.previousCell, Seeds.PlaybackVideo.previousCell) + XCTAssertEqual(spy.presentMoveCellNextResponse.currentCell, Seeds.PlaybackVideo.currentCell) + XCTAssertEqual(spy.presentMoveCellNextResponse.indexPathRow, nil) + if let isTeleport = sut.isTeleport { + XCTAssertFalse(isTeleport) + } else { + XCTFail() + } + } + + func test_playTeleportVideo를_호출하면_presentMoveCellNext를_호출한다_무한스크롤상황일_때() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost] + sut.isTeleport = true + sut.previousCell = Seeds.PlaybackVideo.previousCell + let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: 1, currentCell: Seeds.PlaybackVideo.currentCell) + sut.presenter = spy + + // act + sut.playTeleportVideo(with: request) + + // Assert + XCTAssertTrue(spy.presentMoveCellNextDidCalled, "playTeleportvideo는 presentMoveCellNext를 호출하지 않았습니다") + XCTAssertEqual(spy.presentMoveCellNextResponse.previousCell, Seeds.PlaybackVideo.previousCell) + XCTAssertEqual(spy.presentMoveCellNextResponse.currentCell, Seeds.PlaybackVideo.currentCell) + XCTAssertEqual(sut.previousCell, Seeds.PlaybackVideo.currentCell) + if let isTeleport = sut.isTeleport { + XCTAssertFalse(isTeleport) + } else { + XCTFail() + } + } + + func test_playTeleportVideo를_호출하면_presentMoveCellNext를_호출한다_셀이_삭제되는_상황일_때() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost] + sut.isTeleport = true + sut.previousCell = Seeds.PlaybackVideo.previousCell + let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: 1, currentCell: Seeds.PlaybackVideo.currentCell) + sut.presenter = spy + // act + sut.playTeleportVideo(with: request) + + // Assert + XCTAssertTrue(spy.presentMoveCellNextDidCalled, "playTeleportvideo는 presentMoveCellNext를 호출하지 않았습니다") + XCTAssertEqual(spy.presentMoveCellNextResponse.previousCell, Seeds.PlaybackVideo.previousCell) + XCTAssertEqual(spy.presentMoveCellNextResponse.currentCell, Seeds.PlaybackVideo.currentCell) + XCTAssertEqual(sut.previousCell, Seeds.PlaybackVideo.currentCell) + XCTAssertFalse(((sut.isDelete) != nil)) + } + + func test_resumePlaybackView를_호출할때_previousCell이_존재하면_presentPlayInitialPlaybackCell을_호출한다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.previousCell = Seeds.PlaybackVideo.previousCell + sut.presenter = spy + + // act + sut.resumePlaybackView() + + // Assert + XCTAssertTrue(spy.presentPlayInitialPlaybackCellDidCalled, "resumPlaybackView가 presentPlayInitialPlaybackCell을 호출하지 않았습니다") + XCTAssertEqual(spy.presentPlayInitialPlaybackCellResponse.currentCell, Seeds.PlaybackVideo.previousCell) + XCTAssertEqual(spy.presentPlayInitialPlaybackCellResponse.previousCell, nil) + } + + func test_resumePlaybackView를_호출할때_previousCell이_존재하면_presentSetInitialPlaybackCell을_호출한다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.index = 2 + sut.presenter = spy + + // act + sut.resumePlaybackView() + + // Assert + XCTAssertTrue(spy.presentSetInitialPlaybackCellDidCalled, "resumPlaybackView가 presentSetInitialPlaybackCell을 호출하지 않았습니다") + XCTAssertEqual(spy.presentSetInitialPlaybackCellResponse.indexPathRow, 2) + } + + func test_leavePlaybackView를_호출할때_presentLeavePlaybackView를_호출한다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.previousCell = Seeds.PlaybackVideo.previousCell + sut.presenter = spy + // act + sut.leavePlaybackView() + + // Assert + XCTAssertTrue(spy.presentLeavePlaybackViewDidCalled, "leavePlaybackView가 presentLeavePlaybackView를 호출하지 않았습니다") + XCTAssertEqual(spy.presentLeavePlaybackViewResponse.previousCell, Seeds.PlaybackVideo.previousCell) + } + + func test_resetVideo를_호출할때_presentResetPlaybackCell을_호출한다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.previousCell = Seeds.PlaybackVideo.previousCell + sut.presenter = spy + // act + sut.resetVideo() + + // Assert + XCTAssertTrue(spy.presentResetPlaybackCellDidCalled, "resetVideo가 presentResetPlaybackCell을 호출하지 않았습니다") + XCTAssertEqual(spy.presentResetPlaybackCellResponse.previousCell, nil) + XCTAssertEqual(spy.presentResetPlaybackCellResponse.currentCell, Seeds.PlaybackVideo.previousCell) + } + + func test_careVideoLoading을_호출할때_presentMoveCellNext를_호출하지않는다_previousCell이_존재할때() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.previousCell = Seeds.PlaybackVideo.previousCell + let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: nil, currentCell: nil) + // act + sut.careVideoLoading(with: request) + + // Assert + XCTAssertFalse(spy.presentMoveCellNextDidCalled, "careVideoLoading은 presentMoveCellNext를 호출하지 않았습니다") + } + + func test_careVideoLoading을_호출할때_presentMoveCellNext를_호출하지않는다_previousCell이_존재하지않을때() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.previousCell = nil + sut.presenter = spy + let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: nil, currentCell: Seeds.PlaybackVideo.currentCell) + // act + sut.careVideoLoading(with: request) + + // Assert + XCTAssertTrue(spy.presentMoveCellNextDidCalled, "careVideoLoading은 presentMoveCellNext를 호출하지 않았습니다") + XCTAssertEqual(spy.presentMoveCellNextResponse.previousCell, nil) + XCTAssertEqual(spy.presentMoveCellNextResponse.currentCell, Seeds.PlaybackVideo.currentCell) + } + + func test_configurePlaybackCell을_호출할때_presentConfigureCell을_호출한다_map일때() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost] + sut.parentView = .map + sut.presenter = spy + + // act + sut.configurePlaybackCell() + + // Assert + XCTAssertTrue(spy.presentConfigureCellDidCalled, "configurePlaybackCell은 presentConfigureCell을 호출하지 않았습니다") + XCTAssertEqual(spy.presentConfigureCellResponse.teleportIndex, 4) + } + + func test_configurePlaybackCell을_호출할때_presentConfigureCell을_호출한다_map이_아닐때() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost] + sut.parentView = .home + sut.presenter = spy + + // act + sut.configurePlaybackCell() + + // Assert + XCTAssertTrue(spy.presentConfigureCellDidCalled, "configurePlaybackCell은 presentConfigureCell을 호출하지 않았습니다") + } + + func test_setSeemoreButton을_호출할때_presentSetSeemoreButton을_호출하고_올바른_데이터를_전달한다_본인일경우() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.previousCell = Seeds.PlaybackVideo.previousCell + sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: -1, boardID: -1)] + sut.presenter = spy + + // act + sut.setSeeMoreButton(with: Models.SetSeemoreButton.Request(indexPathRow: 0)) + + // Assert + XCTAssertTrue(spy.presentSetSeemoreButtonDidCalled, "") + XCTAssertEqual(spy.presentSetSeemoreButtonResponse.buttonType, .delete) + } + + func test_본인이아닐경우_setSeemoreButton을_호출할때_presentSetSeemoreButton을_호출하고_올바른_데이터를_전달한다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.previousCell = Seeds.PlaybackVideo.previousCell + sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 1, boardID: -1)] + sut.presenter = spy + + // act + sut.setSeeMoreButton(with: Models.SetSeemoreButton.Request(indexPathRow: 0)) + + // Assert + XCTAssertTrue(spy.presentSetSeemoreButtonDidCalled, "") + XCTAssertEqual(spy.presentSetSeemoreButtonResponse.buttonType, .report) + } + + func test_moveToProfile을_호출할때_presentProfile이_호출된다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + let request: Models.MoveToRelativeView.Request = Models.MoveToRelativeView.Request(indexPathRow: 0, selectedTag: "테스트") + sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 1, boardID: -1)] + sut.presenter = spy + + // act + sut.moveToProfile(with: request) + + // Assert + XCTAssertTrue(spy.presentProfileDidCalled, "moveToProfile이 presentProfileDidCalled를 호출하지 않았습니다") + XCTAssertEqual(sut.memberID, 1) + } + + func test_moveToTagPlay를_호출할때_presentTagPlay가_호출된다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + let request: Models.MoveToRelativeView.Request = Models.MoveToRelativeView.Request(indexPathRow: 0, selectedTag: "테스트") + sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 1, boardID: -1)] + sut.presenter = spy + + // act + sut.moveToTagPlay(with: request) + + // Assert + XCTAssertTrue(spy.presentTagPlayDidCalled, "moveToTagPlay이 presentTagPlay를 호출하지 않았습니다") + XCTAssertEqual(sut.selectedTag, "테스트") + } + +// func test_fetchPosts를_호출하면_presentLoadFetchVideosResponse를_호출한다_Home() async { +// // Arrange +// let spy = PlaybackPresentationLogicSpy() +// sut.parentView = .home +// sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] +// sut.presenter = spy +// sut.isFetchReqeust = false +// sut.currentPage = 1 +// +// // act +// let result = await sut.fetchPosts().value +// +// // Assert +// XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") +// XCTAssertTrue(result, "fetchPost에 실패했습니다.") +// XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) +// XCTAssertEqual(sut.posts?.count, 3) +// } +// +// func test_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다_tag() async throws { +// // Arrange +// let spy = PlaybackPresentationLogicSpy() +// sut.parentView = .tag +// sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2] +// sut.selectedTag = "테스트" +// sut.presenter = spy +// sut.isFetchReqeust = false +// sut.currentPage = 1 +// +// // act +// let result = await sut.fetchPosts().value +// try await Task.sleep(nanoseconds: 3_000_000_000) +// +// // Assert +// XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") +// XCTAssertTrue(result, "fetchPost에 실패했습니다.") +// XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) +// XCTAssertEqual(sut.posts?.count, 21) +// } +// +// func test_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다_Profile() async throws { +// // Arrange +// let spy = PlaybackPresentationLogicSpy() +// sut.parentView = .otherProfile +// sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2] +// sut.presenter = spy +// sut.memberID = -1 +// sut.isFetchReqeust = false +// sut.currentPage = 1 +// +// // act +// let result = await sut.fetchPosts().value +// +// // Assert +// XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") +// XCTAssertTrue(result, "fetchPost에 실패했습니다.") +// XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) +// XCTAssertEqual(sut.posts?.count, 21) +// } +} From 244a6cac3eae0f8aadc8a43a0eeda476ff073bea Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Tue, 9 Jan 2024 22:23:03 +0900 Subject: [PATCH 06/16] =?UTF-8?q?:white=5Fcheck=5Fmark:=20PlaybackInteract?= =?UTF-8?q?or=20Test=202=EC=B0=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover.xcodeproj/project.pbxproj | 4 + .../Scenes/Playback/PlaybackInteractor.swift | 2 +- .../Mocks/Workers/MockPlaybackWorker.swift | 177 +++++++++ .../Workers/PlaybackInteractorTests.swift | 360 +++++++++++++----- iOS/Layover/LayoverTests/Seeds.swift | 2 +- 5 files changed, 448 insertions(+), 97 deletions(-) create mode 100644 iOS/Layover/LayoverTests/Mocks/Workers/MockPlaybackWorker.swift diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index e31f751..2c80ebc 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -147,6 +147,7 @@ 835A61A92B0B5A31002F22A5 /* LoginConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835A61A82B0B5A31002F22A5 /* LoginConfigurator.swift */; }; 8363A32D2B4C1CBB00772DDF /* PlaybackPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */; }; 8363A32F2B4C329100772DDF /* PlaybackInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8363A32E2B4C329100772DDF /* PlaybackInteractorTests.swift */; }; + 8363A3332B4D6E9B00772DDF /* MockPlaybackWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8363A3322B4D6E9B00772DDF /* MockPlaybackWorker.swift */; }; 836C33872B15A29600ECAFB0 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33862B15A29600ECAFB0 /* Toast.swift */; }; 836C338B2B15D22C00ECAFB0 /* PlaybackConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C338A2B15D22C00ECAFB0 /* PlaybackConfigurator.swift */; }; 836C33912B17629400ECAFB0 /* MapRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33902B17629400ECAFB0 /* MapRouter.swift */; }; @@ -406,6 +407,7 @@ 835A61AA2B0B85FD002F22A5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LaunchScreen.strings; sourceTree = ""; }; 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackPresenterTests.swift; path = LayoverTests/Mocks/Workers/PlaybackPresenterTests.swift; sourceTree = SOURCE_ROOT; }; 8363A32E2B4C329100772DDF /* PlaybackInteractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackInteractorTests.swift; path = LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift; sourceTree = SOURCE_ROOT; }; + 8363A3322B4D6E9B00772DDF /* MockPlaybackWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPlaybackWorker.swift; sourceTree = ""; }; 836C33862B15A29600ECAFB0 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; 836C338A2B15D22C00ECAFB0 /* PlaybackConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackConfigurator.swift; sourceTree = ""; }; 836C33902B17629400ECAFB0 /* MapRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRouter.swift; sourceTree = ""; }; @@ -652,6 +654,7 @@ 1925136C2B26F84E001533FA /* MockTagPlayListWorker.swift */, 192513842B27852C001533FA /* MockUserWorker.swift */, 19AE481B2B28C53800DD4612 /* MockSettingWorker.swift */, + 8363A3322B4D6E9B00772DDF /* MockPlaybackWorker.swift */, ); path = Workers; sourceTree = ""; @@ -1602,6 +1605,7 @@ 192513682B26F7CE001533FA /* TagPlayListViewControllerTests.swift in Sources */, 1925136A2B26F7CE001533FA /* TagPlayListWorkerTests.swift in Sources */, 19AE48182B28C2B700DD4612 /* SettingInteractorTests.swift in Sources */, + 8363A3332B4D6E9B00772DDF /* MockPlaybackWorker.swift in Sources */, 192513A72B278BB3001533FA /* Seeds.swift in Sources */, 194C21C52B1DEE6B00C62645 /* HomeWorkerTests.swift in Sources */, 19AE481C2B28C53800DD4612 /* MockSettingWorker.swift in Sources */, diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift index ef93b5a..c714057 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift @@ -278,7 +278,7 @@ final class PlaybackInteractor: PlaybackBusinessLogic, PlaybackDataStore { isNeedReplace: true) playbackVideoInfos.append(playbackVideoInfos[request.indexPathRow - 1]) if let posts { - self.posts?.insert(posts[request.indexPathRow - 1], at: 0) + self.posts?.append(posts[request.indexPathRow - 1]) } } else { response = Models.DeletePlaybackVideo.Response( diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/MockPlaybackWorker.swift b/iOS/Layover/LayoverTests/Mocks/Workers/MockPlaybackWorker.swift new file mode 100644 index 0000000..a8e5d83 --- /dev/null +++ b/iOS/Layover/LayoverTests/Mocks/Workers/MockPlaybackWorker.swift @@ -0,0 +1,177 @@ +// +// MockPlaybackWorker.swift +// Layover +// +// Created by 황지웅 on 12/7/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +@testable import Layover +import Foundation +import OSLog + +final class MockPlaybackWorker: PlaybackWorkerProtocol { + + // MARK: - Properties + + typealias Models = PlaybackModels + + private let provider: ProviderType + private let authManager: AuthManagerProtocol + + // MARK: - Methods + + init(provider: ProviderType = Provider(session: .initMockSession()), authManager: AuthManagerProtocol = StubAuthManager()) { + self.provider = provider + self.authManager = authManager + } + + func makeInfiniteScroll(posts: [Post]) -> [Post] { + var tempVideos: [Post] = [] + for post in posts { + if post.board.videoURL == nil { + continue + } + tempVideos.append(post) + } + guard let tempLastVideo: Post = tempVideos.last, + let tempFirstVideo: Post = tempVideos.first + else { return posts } + tempVideos.insert(tempLastVideo, at: 0) + tempVideos.append(tempFirstVideo) + return tempVideos + } + + func deletePlaybackVideo(boardID: Int) async -> Bool { + guard let mockFileLocation = Bundle(for: type(of: self)).url(forResource: "DeleteVideo", withExtension: "json"), + let mockData = try? Data(contentsOf: mockFileLocation) + else { + os_log(.error, log: .data, "Failed to generate mock with error: %@", "Generate File Error") + return false + } + + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse( + url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: nil) + return (response, mockData, nil) + } + + do { + let endPoint = EndPoint( + path: "/board", + method: .DELETE, + queryParameters: ["boardId": boardID]) + _ = try await provider.request(with: endPoint) + return true + } catch { + os_log(.error, log: .data, "Failed to delete with error%@", error.localizedDescription) + return false + } + } + + func transLocation(latitude: Double, longitude: Double) async -> String? { + "서울특별시" + } + + func fetchImageData(with url: URL?) async -> Data? { + guard let url else { return nil } + do { + guard let imageURL = Bundle(for: type(of: self)).url(forResource: "sample", withExtension: "jpeg") else { + return nil + } + let mockData = try? Data(contentsOf: imageURL) + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: nil) + return (response, mockData, nil) + } + + let data = try await provider.request(url: url) + return data + } catch { + os_log(.error, log: .data, "%@", error.localizedDescription) + return nil + } + } + + func fetchHomePosts() async -> [Post]? { + guard let fileLocation = Bundle(for: type(of: self)).url(forResource: "PostList", withExtension: "json") else { return nil } + do { + let mockData = try Data(contentsOf: fileLocation) + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: nil) + return (response, mockData, nil) + } + let endPoint: EndPoint = EndPoint>(path: "/board/home", + method: .GET) + let response = try await provider.request(with: endPoint) + guard let data = response.data else { return nil } + return data.map { $0.toDomain() } + } catch { + os_log(.error, log: .data, "%@", error.localizedDescription) + return nil + } + } + + func fetchProfilePosts(profileID: Int?, page: Int) async -> [Post]? { + let resourceFileName = switch page { case 1: "PostList" case 2: "PostListMore" default: "PostListEnd" } + guard let fileLocation = Bundle(for: type(of: self)).url(forResource: resourceFileName, withExtension: "json") else { return nil } + do { + let mockData = try Data(contentsOf: fileLocation) + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: nil) + return (response, mockData, nil) + } + let endPoint = EndPoint>(path: "/member/posts", + method: .GET, + queryParameters: ["page": page]) + let response = try await provider.request(with: endPoint) + return response.data?.map { $0.toDomain() } + } catch { + os_log(.error, log: .data, "%@", error.localizedDescription) + return nil + } + } + + func fetchTagPosts(selectedTag: String, page: Int) async -> [Post]? { + let resourceFileName = switch page { case 1: "PostList" case 2: "PostListMore" default: "PostListEnd" } + guard let fileLocation = Bundle(for: type(of: self)).url(forResource: resourceFileName, withExtension: "json") else { return nil } + + do { + let mockData = try? Data(contentsOf: fileLocation) + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: nil) + return (response, mockData, nil) + } + + let endPoint = EndPoint>(path: "/board/tag", + method: .GET, + queryParameters: ["tag": selectedTag]) + + let response = try await provider.request(with: endPoint) + return response.data?.map { $0.toDomain() } + } catch { + os_log(.error, log: .data, "%@", error.localizedDescription) + return nil + } + } + + func isMyVideo(currentCellMemberID: Int) -> Bool { + return currentCellMemberID == authManager.memberID + } + +} diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift index 1483b34..2742637 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift @@ -311,43 +311,40 @@ final class PlaybackInteractorTests: XCTestCase { XCTAssertEqual(spy.presentShowPlayerSliderResponse.currentCell, request.currentCell) } -// func test_playVideo를_호출하면_presentMoveCellNext를_호출한다_parentView가_map이고_index가_마지막을_가리킬_떄() { -// // Arrange -// let spy = PlaybackPresentationLogicSpy() -// sut.parentView = .map -// sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] -// sut.previousCell = Seeds.PlaybackVideo.previousCell -// let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: 1, currentCell: Seeds.PlaybackVideo.currentCell) -// sut.presenter = spy -// // act -// sut.playVideo(with: request) -// -// // Assert -// XCTAssertTrue(spy.presentTeleportCellDidCalled, "playVideo는 presentTeleportCell를 호출하지 않았습니다") -// XCTAssertEqual(sut.previousCell, Seeds.PlaybackVideo.previousCell) -// XCTAssertEqual(spy.presentTeleportCellResponse.previousCell, Seeds.PlaybackVideo.previousCell) -// XCTAssertEqual(spy.presentTeleportCellResponse.indexPathRow, 1) -// XCTAssertTrue(((sut.isTeleport) != nil)) -// } - -// func test_playVideo를_호출하면_presentMoveCellNext를_호출한다_parentView가_map이고_index가_처음을_가리킬_떄() { -// // Arrange -// let spy = PlaybackPresentationLogicSpy() -// sut.parentView = .map -// sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] -// sut.previousCell = Seeds.PlaybackVideo.previousCell -// let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: 1, currentCell: Seeds.PlaybackVideo.currentCell) -// sut.presenter = spy -// // act -// sut.playVideo(with: request) -// -// // Assert -// XCTAssertTrue(spy.presentTeleportCellDidCalled, "playVideo는 presentTeleportCell를 호출하지 않았습니다") -// XCTAssertEqual(spy.presentTeleportCellResponse.previousCell, Seeds.PlaybackVideo.previousCell) -// XCTAssertEqual(spy.presentTeleportCellResponse.currentCell, nil) -// XCTAssertEqual(spy.presentTeleportCellResponse.indexPathRow, 0) -// XCTAssertTrue(((sut.isTeleport) != nil)) -// } + func test_parentView가_map이고_index가_마지막을_가리킬_떄_playVideo를_호출하면_presentMoveCellNext를_호출한다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.parentView = .map + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] + sut.previousCell = Seeds.PlaybackVideo.previousCell + let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: 1, currentCell: Seeds.PlaybackVideo.currentCell) + sut.presenter = spy + // act + sut.playVideo(with: request) + + // Assert + XCTAssertTrue(spy.presentMoveCellNextDidCalled, "playVideo는 presentTeleportCell를 호출하지 않았습니다") + XCTAssertEqual(spy.presentMoveCellNextResponse.previousCell, Seeds.PlaybackVideo.previousCell) + XCTAssertFalse(sut.isTeleport!) + } + + func test_parentView가_map이고_index가_처음을_가리킬_떄_playVideo를_호출하면_presentMoveCellNext를_호출한다() { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.parentView = .map + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] + sut.previousCell = Seeds.PlaybackVideo.previousCell + let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: 1, currentCell: Seeds.PlaybackVideo.currentCell) + sut.presenter = spy + // act + sut.playVideo(with: request) + + // Assert + XCTAssertTrue(spy.presentMoveCellNextDidCalled, "playVideo는 presentTeleportCell를 호출하지 않았습니다") + XCTAssertEqual(spy.presentMoveCellNextResponse.previousCell, Seeds.PlaybackVideo.previousCell) + XCTAssertEqual(spy.presentMoveCellNextResponse.currentCell, Seeds.PlaybackVideo.currentCell) + XCTAssertFalse(sut.isTeleport!) + } func test_playVideo를_호출하면_presentMoveCellNext를_호출한다_parentView가_map이고_텔레포트가_필요없는_상황일_때() { // Arrange @@ -590,63 +587,236 @@ final class PlaybackInteractorTests: XCTestCase { XCTAssertEqual(sut.selectedTag, "테스트") } -// func test_fetchPosts를_호출하면_presentLoadFetchVideosResponse를_호출한다_Home() async { -// // Arrange -// let spy = PlaybackPresentationLogicSpy() -// sut.parentView = .home -// sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] -// sut.presenter = spy -// sut.isFetchReqeust = false -// sut.currentPage = 1 -// -// // act -// let result = await sut.fetchPosts().value -// -// // Assert -// XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") -// XCTAssertTrue(result, "fetchPost에 실패했습니다.") -// XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) -// XCTAssertEqual(sut.posts?.count, 3) -// } -// -// func test_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다_tag() async throws { -// // Arrange -// let spy = PlaybackPresentationLogicSpy() -// sut.parentView = .tag -// sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2] -// sut.selectedTag = "테스트" -// sut.presenter = spy -// sut.isFetchReqeust = false -// sut.currentPage = 1 -// -// // act -// let result = await sut.fetchPosts().value + func test_Home_fetchPosts를_호출하면_presentLoadFetchVideosResponse를_호출한다() async { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.parentView = .home + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] + sut.presenter = spy + + // act + await sut.fetchPosts() + + // Assert + XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") + XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) + XCTAssertEqual(sut.posts?.count, 3) + } + + func test_tag_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다() async throws { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.parentView = .tag + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2] + sut.playbackVideoInfos = [ + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0) + ] + sut.selectedTag = "테스트" + sut.presenter = spy + + // act + await sut.fetchPosts() // try await Task.sleep(nanoseconds: 3_000_000_000) -// -// // Assert -// XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") -// XCTAssertTrue(result, "fetchPost에 실패했습니다.") -// XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) -// XCTAssertEqual(sut.posts?.count, 21) -// } -// -// func test_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다_Profile() async throws { -// // Arrange -// let spy = PlaybackPresentationLogicSpy() -// sut.parentView = .otherProfile -// sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2] -// sut.presenter = spy -// sut.memberID = -1 -// sut.isFetchReqeust = false -// sut.currentPage = 1 -// -// // act -// let result = await sut.fetchPosts().value -// -// // Assert -// XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") -// XCTAssertTrue(result, "fetchPost에 실패했습니다.") -// XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) -// XCTAssertEqual(sut.posts?.count, 21) -// } + + // Assert + XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") + XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) + XCTAssertEqual(sut.posts?.count, 21) + XCTAssertEqual(sut.playbackVideoInfos.count, 21) + } + + func test_Profile_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다() async throws { + // Arrange + let spy = PlaybackPresentationLogicSpy() + sut.parentView = .otherProfile + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.post1, Seeds.Posts.post2] + sut.playbackVideoInfos = [ + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0), + Models.PlaybackInfo(memberID: 0, boardID: 0), Models.PlaybackInfo(memberID: 0, boardID: 0) + ] + + sut.presenter = spy + sut.memberID = -1 + + // act + await sut.fetchPosts() + + // Assert + XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") + XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) + XCTAssertEqual(sut.posts?.count, 21) + } + + func test_map이_아닐_때_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async throws { + // Arrange + let spy = PlaybackPresentationLogicSpy() + let testPost: Post = Seeds.Posts.post1 + let playbackVideo: Models.PlaybackVideo = Models.PlaybackVideo( + displayedPost: Models.DisplayedPost( + member: Models.Member( + memberID: testPost.member.identifier, + username: testPost.member.username, + profileImageURL: testPost.member.profileImageURL), + board: Models.Board( + boardID: testPost.board.identifier, + title: testPost.board.title, + description: testPost.board.description, + videoURL: testPost.board.videoURL!, + latitude: testPost.board.latitude, + longitude: testPost.board.longitude), + tags: Seeds.Posts.post1.tag)) + sut.parentView = .home + sut.presenter = spy + sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] + sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 0, boardID: 1), Models.PlaybackInfo(memberID: 0, boardID: 2)] + + // act + await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 0)) + + // Assert + XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") + XCTAssertTrue(spy.presentDeleteVideoResponse.result) + XCTAssertEqual(spy.presentDeleteVideoResponse.playbackVideo, playbackVideo) + XCTAssertEqual(sut.posts!.count, 1) + XCTAssertEqual(sut.playbackVideoInfos.count, 1) + } + + // TODO: map paging추가되면 로직 그냥 날려버리기 + + func test_map일_때_마지막_무한스크롤셀을_지울_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async { + // Arrange + let spy = PlaybackPresentationLogicSpy() + let testPost: Post = Seeds.Posts.post1 + let playbackVideo: Models.PlaybackVideo = Models.PlaybackVideo( + displayedPost: Models.DisplayedPost( + member: Models.Member( + memberID: testPost.member.identifier, + username: testPost.member.username, + profileImageURL: testPost.member.profileImageURL), + board: Models.Board( + boardID: testPost.board.identifier, + title: testPost.board.title, + description: testPost.board.description, + videoURL: testPost.board.videoURL!, + latitude: testPost.board.latitude, + longitude: testPost.board.longitude), + tags: Seeds.Posts.post1.tag)) + sut.parentView = .map + sut.presenter = spy + sut.posts = [Seeds.Posts.thumbnailImageNilPost, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost ,Seeds.Posts.post1] + sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1), Models.PlaybackInfo(memberID: 0, boardID: 2), Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1)] + + // act + await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 3)) + + // assert + XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") + XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 1) + XCTAssertEqual(sut.playbackVideoInfos[1].boardID, 2) + XCTAssertEqual(sut.playbackVideoInfos[2].boardID, 1) + XCTAssertEqual(sut.playbackVideoInfos[3].boardID, 2) + XCTAssertEqual(sut.posts?[0].board.identifier, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(sut.posts?[1].board.identifier, Seeds.Posts.post2.board.identifier) + XCTAssertEqual(sut.posts?[2].board.identifier, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(sut.posts?[3].board.identifier, Seeds.Posts.post2.board.identifier) + XCTAssertEqual(sut.playbackVideoInfos.count, 4) + XCTAssertEqual(sut.posts?.count, 4) + } + + func test_map일_때_첫번째_무한스크롤셀을_지울_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async { + // Arrange + let spy = PlaybackPresentationLogicSpy() + let testPost: Post = Seeds.Posts.post1 + let playbackVideo: Models.PlaybackVideo = Models.PlaybackVideo( + displayedPost: Models.DisplayedPost( + member: Models.Member( + memberID: testPost.member.identifier, + username: testPost.member.username, + profileImageURL: testPost.member.profileImageURL), + board: Models.Board( + boardID: testPost.board.identifier, + title: testPost.board.title, + description: testPost.board.description, + videoURL: testPost.board.videoURL!, + latitude: testPost.board.latitude, + longitude: testPost.board.longitude), + tags: Seeds.Posts.post1.tag)) + sut.parentView = .map + sut.presenter = spy + sut.posts = [Seeds.Posts.thumbnailImageNilPost, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost ,Seeds.Posts.post1] + sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1), Models.PlaybackInfo(memberID: 0, boardID: 2), Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1)] + + // act + await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 1)) + + // assert + XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") + XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 3) + XCTAssertEqual(sut.playbackVideoInfos[1].boardID, 2) + XCTAssertEqual(sut.playbackVideoInfos[2].boardID, 3) + XCTAssertEqual(sut.playbackVideoInfos[3].boardID, 2) + XCTAssertEqual(sut.posts?[0].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) + XCTAssertEqual(sut.posts?[1].board.identifier, Seeds.Posts.post2.board.identifier) + XCTAssertEqual(sut.posts?[2].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) + XCTAssertEqual(sut.posts?[3].board.identifier, Seeds.Posts.post2.board.identifier) + XCTAssertEqual(sut.playbackVideoInfos.count, 4) + XCTAssertEqual(sut.posts?.count, 4) + } + + func test_map일_때_무한스크롤셀을_지우지않을_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async { + // Arrange + let spy = PlaybackPresentationLogicSpy() + let testPost: Post = Seeds.Posts.post1 + let playbackVideo: Models.PlaybackVideo = Models.PlaybackVideo( + displayedPost: Models.DisplayedPost( + member: Models.Member( + memberID: testPost.member.identifier, + username: testPost.member.username, + profileImageURL: testPost.member.profileImageURL), + board: Models.Board( + boardID: testPost.board.identifier, + title: testPost.board.title, + description: testPost.board.description, + videoURL: testPost.board.videoURL!, + latitude: testPost.board.latitude, + longitude: testPost.board.longitude), + tags: Seeds.Posts.post1.tag)) + sut.parentView = .map + sut.presenter = spy + sut.posts = [Seeds.Posts.thumbnailImageNilPost, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost ,Seeds.Posts.post1] + sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1), Models.PlaybackInfo(memberID: 0, boardID: 2), Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1)] + + // act + await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 2)) + + // assert + XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") + XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 3) + XCTAssertEqual(sut.playbackVideoInfos[1].boardID, 1) + XCTAssertEqual(sut.playbackVideoInfos[2].boardID, 3) + XCTAssertEqual(sut.playbackVideoInfos[3].boardID, 1) + XCTAssertEqual(sut.posts?[0].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) + XCTAssertEqual(sut.posts?[1].board.identifier, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(sut.posts?[2].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) + XCTAssertEqual(sut.posts?[3].board.identifier, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(sut.playbackVideoInfos.count, 4) + XCTAssertEqual(sut.posts?.count, 4) + } } diff --git a/iOS/Layover/LayoverTests/Seeds.swift b/iOS/Layover/LayoverTests/Seeds.swift index ec3ed8b..b955342 100644 --- a/iOS/Layover/LayoverTests/Seeds.swift +++ b/iOS/Layover/LayoverTests/Seeds.swift @@ -49,7 +49,7 @@ class Seeds { username: "안유진", introduce: "안녕하세요, 아이브의 안유진입니다.", profileImageURL: URL(string: "https://cdn.footballist.co.kr/news/photo/202307/170226_100422_1733.jpg")!), - board: Board(identifier: 1, + board: Board(identifier: 3, title: "최강 아이돌", description: "게시글 설명", thumbnailImageURL: nil, From eaa59c03e0b322a413ebc060dafb4b3053f25f4d Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Thu, 11 Jan 2024 00:33:59 +0900 Subject: [PATCH 07/16] =?UTF-8?q?:recycle:=20=EB=A6=AC=EB=B7=B0=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 유경님 리뷰사항 반영 --- .../LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift index 2742637..4404170 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift @@ -400,7 +400,7 @@ final class PlaybackInteractorTests: XCTestCase { // Arrange let spy = PlaybackPresentationLogicSpy() sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost] - sut.isTeleport = true + sut.isDelete = true sut.previousCell = Seeds.PlaybackVideo.previousCell let request: Models.DisplayPlaybackVideo.Request = Models.DisplayPlaybackVideo.Request(indexPathRow: 1, currentCell: Seeds.PlaybackVideo.currentCell) sut.presenter = spy @@ -412,7 +412,7 @@ final class PlaybackInteractorTests: XCTestCase { XCTAssertEqual(spy.presentMoveCellNextResponse.previousCell, Seeds.PlaybackVideo.previousCell) XCTAssertEqual(spy.presentMoveCellNextResponse.currentCell, Seeds.PlaybackVideo.currentCell) XCTAssertEqual(sut.previousCell, Seeds.PlaybackVideo.currentCell) - XCTAssertFalse(((sut.isDelete) != nil)) + XCTAssertFalse(sut.isDelete!) } func test_resumePlaybackView를_호출할때_previousCell이_존재하면_presentPlayInitialPlaybackCell을_호출한다() { @@ -625,7 +625,6 @@ final class PlaybackInteractorTests: XCTestCase { // act await sut.fetchPosts() -// try await Task.sleep(nanoseconds: 3_000_000_000) // Assert XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") From 99445c7e6cb6bbe48d543cf6163033a49e981df0 Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Thu, 11 Jan 2024 00:49:16 +0900 Subject: [PATCH 08/16] =?UTF-8?q?:wrench:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=ED=8E=98=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift index 4404170..aebb427 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift @@ -409,7 +409,7 @@ final class PlaybackInteractorTests: XCTestCase { // Assert XCTAssertTrue(spy.presentMoveCellNextDidCalled, "playTeleportvideo는 presentMoveCellNext를 호출하지 않았습니다") - XCTAssertEqual(spy.presentMoveCellNextResponse.previousCell, Seeds.PlaybackVideo.previousCell) + XCTAssertNil(spy.presentMoveCellNextResponse.previousCell) XCTAssertEqual(spy.presentMoveCellNextResponse.currentCell, Seeds.PlaybackVideo.currentCell) XCTAssertEqual(sut.previousCell, Seeds.PlaybackVideo.currentCell) XCTAssertFalse(sut.isDelete!) From 46b51678f11877e0243743361808e7d746ed009d Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Thu, 11 Jan 2024 01:05:23 +0900 Subject: [PATCH 09/16] =?UTF-8?q?:white=5Fcheck=5Fmark:=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=9B=90=EC=9D=B8=20=EC=B0=BE=EA=B8=B0=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift index aebb427..f2cc634 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift @@ -587,7 +587,7 @@ final class PlaybackInteractorTests: XCTestCase { XCTAssertEqual(sut.selectedTag, "테스트") } - func test_Home_fetchPosts를_호출하면_presentLoadFetchVideosResponse를_호출한다() async { + func test_Home_fetchPosts를_호출하면_presentLoadFetchVideosResponse를_호출한다() async throws { // Arrange let spy = PlaybackPresentationLogicSpy() sut.parentView = .home @@ -597,6 +597,8 @@ final class PlaybackInteractorTests: XCTestCase { // act await sut.fetchPosts() + try await Task.sleep(nanoseconds: 3_000_000_000) + // Assert XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) From c3e55ef13e67e59da2cdb0fade9c3f51dab1f5b6 Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Thu, 11 Jan 2024 01:21:30 +0900 Subject: [PATCH 10/16] =?UTF-8?q?:wrench:=20Task=20sleep=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Workers/PlaybackInteractorTests.swift | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift index f2cc634..2f5787a 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift @@ -180,7 +180,7 @@ final class PlaybackInteractorTests: XCTestCase { // MARK: - Tests - func test_parentView가_map일_때_displayVideoList를_호출하면_presentVideoList를_호출하고_올바른_데이터를_전달한다() async { + func test_parentView가_map일_때_displayVideoList를_호출하면_presentVideoList를_호출하고_올바른_데이터를_전달한다() async throws { // Arrange let spy = PlaybackPresentationLogicSpy() sut.presenter = spy @@ -190,6 +190,8 @@ final class PlaybackInteractorTests: XCTestCase { // Act await sut.displayVideoList() + try await Task.sleep(nanoseconds: 3_000_000_000) + // Assert XCTAssertTrue(spy.presentVideoListDidCalled, "displayVideoList는 presentVideoList를 호출하지 않았습니다") // map이므로 2 + 더미셀 2 = 4 @@ -202,7 +204,7 @@ final class PlaybackInteractorTests: XCTestCase { XCTAssertEqual(spy.presentVideoListResponse.videos.last!.displayedPost, Seeds.PlaybackVideos.videos.first!.displayedPost) } - func test_displayVideoList를_호출하면_presentVideoList를_호출하고_올바른_데이터를_전달한다_parentView가_map이_아닐때() async { + func test_displayVideoList를_호출하면_presentVideoList를_호출하고_올바른_데이터를_전달한다_parentView가_map이_아닐때() async throws { // Arrange let spy = PlaybackPresentationLogicSpy() sut.presenter = spy @@ -212,6 +214,8 @@ final class PlaybackInteractorTests: XCTestCase { // Act await sut.displayVideoList() + try await Task.sleep(nanoseconds: 3_000_000_000) + // Assert XCTAssertTrue(spy.presentVideoListDidCalled, "displayVideoList는 presentVideoList를 호출하지 않았습니다") // map이므로 2 + 더미셀 2 = 4 @@ -628,6 +632,9 @@ final class PlaybackInteractorTests: XCTestCase { // act await sut.fetchPosts() + try await Task.sleep(nanoseconds: 3_000_000_000) + + // Assert XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) @@ -659,6 +666,8 @@ final class PlaybackInteractorTests: XCTestCase { // act await sut.fetchPosts() + try await Task.sleep(nanoseconds: 3_000_000_000) + // Assert XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) @@ -691,6 +700,8 @@ final class PlaybackInteractorTests: XCTestCase { // act await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 0)) + try await Task.sleep(nanoseconds: 3_000_000_000) + // Assert XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") XCTAssertTrue(spy.presentDeleteVideoResponse.result) @@ -701,7 +712,7 @@ final class PlaybackInteractorTests: XCTestCase { // TODO: map paging추가되면 로직 그냥 날려버리기 - func test_map일_때_마지막_무한스크롤셀을_지울_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async { + func test_map일_때_마지막_무한스크롤셀을_지울_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async throws { // Arrange let spy = PlaybackPresentationLogicSpy() let testPost: Post = Seeds.Posts.post1 @@ -727,6 +738,9 @@ final class PlaybackInteractorTests: XCTestCase { // act await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 3)) + try await Task.sleep(nanoseconds: 3_000_000_000) + + // assert XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 1) @@ -741,7 +755,7 @@ final class PlaybackInteractorTests: XCTestCase { XCTAssertEqual(sut.posts?.count, 4) } - func test_map일_때_첫번째_무한스크롤셀을_지울_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async { + func test_map일_때_첫번째_무한스크롤셀을_지울_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async throws { // Arrange let spy = PlaybackPresentationLogicSpy() let testPost: Post = Seeds.Posts.post1 @@ -767,6 +781,8 @@ final class PlaybackInteractorTests: XCTestCase { // act await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 1)) + try await Task.sleep(nanoseconds: 3_000_000_000) + // assert XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 3) @@ -781,7 +797,7 @@ final class PlaybackInteractorTests: XCTestCase { XCTAssertEqual(sut.posts?.count, 4) } - func test_map일_때_무한스크롤셀을_지우지않을_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async { + func test_map일_때_무한스크롤셀을_지우지않을_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async throws { // Arrange let spy = PlaybackPresentationLogicSpy() let testPost: Post = Seeds.Posts.post1 @@ -807,6 +823,8 @@ final class PlaybackInteractorTests: XCTestCase { // act await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 2)) + try await Task.sleep(nanoseconds: 3_000_000_000) + // assert XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 3) From c918fd0a65e06a0d16948ef06d6b7bf612a8e347 Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:28:18 +0900 Subject: [PATCH 11/16] =?UTF-8?q?:wrench:=20MockPlaybackWorker=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Mocks/Workers/MockPlaybackWorker.swift | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/MockPlaybackWorker.swift b/iOS/Layover/LayoverTests/Mocks/Workers/MockPlaybackWorker.swift index a8e5d83..5571a21 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/MockPlaybackWorker.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/MockPlaybackWorker.swift @@ -100,7 +100,12 @@ final class MockPlaybackWorker: PlaybackWorkerProtocol { } func fetchHomePosts() async -> [Post]? { - guard let fileLocation = Bundle(for: type(of: self)).url(forResource: "PostList", withExtension: "json") else { return nil } + guard let fileLocation = Bundle(for: type(of: self)).url(forResource: "PostList", + withExtension: "json"), + let imageDataLocation = Bundle(for: type(of: self)).url(forResource: "sample", + withExtension: "jpeg") + else { return nil } + do { let mockData = try Data(contentsOf: fileLocation) MockURLProtocol.requestHandler = { request in @@ -113,8 +118,15 @@ final class MockPlaybackWorker: PlaybackWorkerProtocol { let endPoint: EndPoint = EndPoint>(path: "/board/home", method: .GET) let response = try await provider.request(with: endPoint) - guard let data = response.data else { return nil } - return data.map { $0.toDomain() } + guard let responseData = response.data else { return nil } + + let data = responseData.map { + var domainData = $0.toDomain() + domainData.thumbnailImageData = try? Data(contentsOf: imageDataLocation) + return domainData + } + + return data } catch { os_log(.error, log: .data, "%@", error.localizedDescription) return nil From 7cf0958f27e5c1613ad5614661cb617bd01146ed Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Thu, 11 Jan 2024 18:17:06 +0900 Subject: [PATCH 12/16] =?UTF-8?q?:test=5Ftube:=20Playback=20Worker?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover.xcodeproj/project.pbxproj | 4 + .../Mocks/Workers/PlaybackWorkerTests.swift | 132 ++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index 2c80ebc..54c6178 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -148,6 +148,7 @@ 8363A32D2B4C1CBB00772DDF /* PlaybackPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */; }; 8363A32F2B4C329100772DDF /* PlaybackInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8363A32E2B4C329100772DDF /* PlaybackInteractorTests.swift */; }; 8363A3332B4D6E9B00772DDF /* MockPlaybackWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8363A3322B4D6E9B00772DDF /* MockPlaybackWorker.swift */; }; + 8363A3352B4FE14C00772DDF /* PlaybackWorkerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8363A3342B4FE14C00772DDF /* PlaybackWorkerTests.swift */; }; 836C33872B15A29600ECAFB0 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33862B15A29600ECAFB0 /* Toast.swift */; }; 836C338B2B15D22C00ECAFB0 /* PlaybackConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C338A2B15D22C00ECAFB0 /* PlaybackConfigurator.swift */; }; 836C33912B17629400ECAFB0 /* MapRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33902B17629400ECAFB0 /* MapRouter.swift */; }; @@ -408,6 +409,7 @@ 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackPresenterTests.swift; path = LayoverTests/Mocks/Workers/PlaybackPresenterTests.swift; sourceTree = SOURCE_ROOT; }; 8363A32E2B4C329100772DDF /* PlaybackInteractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackInteractorTests.swift; path = LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift; sourceTree = SOURCE_ROOT; }; 8363A3322B4D6E9B00772DDF /* MockPlaybackWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPlaybackWorker.swift; sourceTree = ""; }; + 8363A3342B4FE14C00772DDF /* PlaybackWorkerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackWorkerTests.swift; path = LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift; sourceTree = SOURCE_ROOT; }; 836C33862B15A29600ECAFB0 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; 836C338A2B15D22C00ECAFB0 /* PlaybackConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackConfigurator.swift; sourceTree = ""; }; 836C33902B17629400ECAFB0 /* MapRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRouter.swift; sourceTree = ""; }; @@ -882,6 +884,7 @@ children = ( 8363A32C2B4C1CBA00772DDF /* PlaybackPresenterTests.swift */, 8363A32E2B4C329100772DDF /* PlaybackInteractorTests.swift */, + 8363A3342B4FE14C00772DDF /* PlaybackWorkerTests.swift */, ); path = Playback; sourceTree = ""; @@ -1603,6 +1606,7 @@ FC4E0C0C2B282AE500152596 /* UploadPostViewControllerTests.swift in Sources */, 1925136B2B26F7CE001533FA /* TagPlayListPresenterTests.swift in Sources */, 192513682B26F7CE001533FA /* TagPlayListViewControllerTests.swift in Sources */, + 8363A3352B4FE14C00772DDF /* PlaybackWorkerTests.swift in Sources */, 1925136A2B26F7CE001533FA /* TagPlayListWorkerTests.swift in Sources */, 19AE48182B28C2B700DD4612 /* SettingInteractorTests.swift in Sources */, 8363A3332B4D6E9B00772DDF /* MockPlaybackWorker.swift in Sources */, diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift new file mode 100644 index 0000000..f8d90a4 --- /dev/null +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift @@ -0,0 +1,132 @@ +// +// PlaybackWorkerTests.swift +// LayoverTests +// +// Created by 황지웅 on 1/11/24. +// Copyright © 2024 CodeBomber. All rights reserved. +// + +@testable import Layover +import XCTest + +final class PlaybackWorkerTests: XCTestCase { + // MARK: Subject under test + + typealias Models = PlaybackModels + var sut: PlaybackWorker! + + // MARK: - Test lifecycle + + override func setUp() { + super.setUp() + setupPlaybackWorker() + } + + override func tearDown() { + super.tearDown() + } + + // MARK: - Test setup + + func setupPlaybackWorker() { + sut = PlaybackWorker(provider: Provider(session: .initMockSession()), authManager: StubAuthManager()) + } + + func test_makeInfiniteScroll은_올바른_결과를_반환한다() { + // Arrange + let testPosts: [Post] = [Seeds.Posts.post1, Seeds.Posts.post2] + + // act + let result: [Post] = sut.makeInfiniteScroll(posts: testPosts) + + XCTAssertEqual(result.count, 4) + XCTAssertEqual(result[0].board.identifier, Seeds.Posts.post2.board.identifier) + XCTAssertEqual(result[1].board.identifier, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(result[2].board.identifier, Seeds.Posts.post2.board.identifier) + XCTAssertEqual(result[3].board.identifier, Seeds.Posts.post1.board.identifier) + } + + func test_deletePlaybackVideo는_올바른_동작을_반환한다() async { + // Arrange + guard let mockFileLocation = Bundle(for: type(of: self)).url(forResource: "DeleteVideo", withExtension: "json"), + let mockData = try? Data(contentsOf: mockFileLocation) else { + XCTFail("Mock json 파일 로드 실패.") + return + } + + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: nil) + return (response, mockData, nil) + } + + let response = await sut.deletePlaybackVideo(boardID: 1) + + XCTAssertNotNil(response, "deleteVideo가 response를 정상적으로 반환하지 못함") + } + + func test_fetchHomePosts는_올바른_목록을_반환한다() async { + // Arrange + guard let mockFileLocation = Bundle(for: type(of: self)).url(forResource: "PostList", withExtension: "json"), + let mockData = try? Data(contentsOf: mockFileLocation) else { + XCTFail("Mock json 파일 로드 실패.") + return + } + + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: nil) + return (response, mockData, nil) + } + + var result: [Post]? + + // act + result = await sut.fetchHomePosts() + + // assert + XCTAssertNotNil(result) + XCTAssertEqual(result?.count, 1, "fetchPost가 성공적으로 데이터를 받아와서 올바른 데이터 갯수를 리턴하지 못했다.") + XCTAssertEqual(result![0].tag, Seeds.Posts.post1.tag, "fetchPost가 성공적으로 데이터를 받아와서 올바른 tag를 리턴하지 못했다.") + XCTAssertEqual(result![0].board.thumbnailImageURL, Seeds.Posts.post1.board.thumbnailImageURL, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.identifier, Seeds.Posts.post1.board.identifier, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.title, Seeds.Posts.post1.board.title, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.description, Seeds.Posts.post1.board.description, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.videoURL, Seeds.Posts.post1.board.videoURL, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.latitude, Seeds.Posts.post1.board.latitude, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.longitude, Seeds.Posts.post1.board.longitude, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.status, Seeds.Posts.post1.board.status, "fetchPost가 성공적으로 데이터를 받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].member.identifier, Seeds.Posts.post1.member.identifier, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].member.username, Seeds.Posts.post1.member.username, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].member.introduce, Seeds.Posts.post1.member.introduce, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].member.profileImageURL, Seeds.Posts.post1.member.profileImageURL, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + } + + func test_fetchProfilePosts_는_올바른_결과를_반환한다() async { + // Arrange + guard let mockFileLocation = Bundle(for: type(of: self)).url(forResource: "PostListMore", withExtension: "json"), + let mockData = try? Data(contentsOf: mockFileLocation) + else { + XCTFail("Mock json 파일 로드 실패.") + return + } + + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: nil) + return (response, mockData, nil) + } + + // act + let result = await sut.fetchProfilePosts(profileID: 2, page: 1) + + // assert + XCTAssertNotNil(result) + } +} From b45bb1a4064441b31a5c3eb033caed11d5578718 Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Thu, 11 Jan 2024 19:37:19 +0900 Subject: [PATCH 13/16] =?UTF-8?q?:wrench:=20Test=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover.xcodeproj/project.pbxproj | 2 ++ .../xcshareddata/xcschemes/Layover.xcscheme | 9 ++++-- iOS/Layover/Layover.xctestplan | 28 +++++++++++++++++++ .../Workers/PlaybackInteractorTests.swift | 2 -- .../Mocks/Workers/PlaybackWorkerTests.swift | 4 +-- 5 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 iOS/Layover/Layover.xctestplan diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index 54c6178..24af78d 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -410,6 +410,7 @@ 8363A32E2B4C329100772DDF /* PlaybackInteractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackInteractorTests.swift; path = LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift; sourceTree = SOURCE_ROOT; }; 8363A3322B4D6E9B00772DDF /* MockPlaybackWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPlaybackWorker.swift; sourceTree = ""; }; 8363A3342B4FE14C00772DDF /* PlaybackWorkerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackWorkerTests.swift; path = LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift; sourceTree = SOURCE_ROOT; }; + 8363A3362B4FFBF200772DDF /* Layover.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Layover.xctestplan; sourceTree = ""; }; 836C33862B15A29600ECAFB0 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; 836C338A2B15D22C00ECAFB0 /* PlaybackConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackConfigurator.swift; sourceTree = ""; }; 836C33902B17629400ECAFB0 /* MapRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRouter.swift; sourceTree = ""; }; @@ -1064,6 +1065,7 @@ FC7E452D2AFEB623004F155A = { isa = PBXGroup; children = ( + 8363A3362B4FFBF200772DDF /* Layover.xctestplan */, 19AACFC52B0F71DF0088143E /* Secrets.xcconfig */, FC7E45692AFEC06E004F155A /* .swiftlint.yml */, FC7E45382AFEB623004F155A /* Layover */, diff --git a/iOS/Layover/Layover.xcodeproj/xcshareddata/xcschemes/Layover.xcscheme b/iOS/Layover/Layover.xcodeproj/xcshareddata/xcschemes/Layover.xcscheme index b985a9e..d8a6c35 100644 --- a/iOS/Layover/Layover.xcodeproj/xcshareddata/xcschemes/Layover.xcscheme +++ b/iOS/Layover/Layover.xcodeproj/xcshareddata/xcschemes/Layover.xcscheme @@ -26,8 +26,13 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - shouldAutocreateTestPlan = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> + + + + Date: Thu, 11 Jan 2024 19:50:51 +0900 Subject: [PATCH 14/16] =?UTF-8?q?:wrench:=20Task=20=EA=B0=90=EC=8B=B8?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Mocks/Workers/PlaybackInteractorTests.swift | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift index b19d6b0..0afcba1 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift @@ -591,7 +591,7 @@ final class PlaybackInteractorTests: XCTestCase { XCTAssertEqual(sut.selectedTag, "테스트") } - func test_Home_fetchPosts를_호출하면_presentLoadFetchVideosResponse를_호출한다() async throws { + func test_Home_fetchPosts를_호출하면_presentLoadFetchVideosResponse를_호출한다() { // Arrange let spy = PlaybackPresentationLogicSpy() sut.parentView = .home @@ -599,12 +599,13 @@ final class PlaybackInteractorTests: XCTestCase { sut.presenter = spy // act - await sut.fetchPosts() - - // Assert - XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") - XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) - XCTAssertEqual(sut.posts?.count, 3) + Task { + await sut.fetchPosts() + // Assert + XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") + XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) + XCTAssertEqual(sut.posts?.count, 3) + } } func test_tag_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다() async throws { @@ -630,7 +631,7 @@ final class PlaybackInteractorTests: XCTestCase { // act await sut.fetchPosts() - try await Task.sleep(nanoseconds: 3_000_000_000) + try await Task.sleep(nanoseconds: 30_000_000_000) // Assert From a94f8f7b536c915eb3ba6cc2eac76dcc61967411 Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Thu, 11 Jan 2024 19:59:41 +0900 Subject: [PATCH 15/16] =?UTF-8?q?:wrench:=20Task=20=EA=B0=90=EC=8B=B8?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Workers/PlaybackInteractorTests.swift | 172 +++++++++--------- .../Mocks/Workers/PlaybackWorkerTests.swift | 62 ++++--- 2 files changed, 118 insertions(+), 116 deletions(-) diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift index 0afcba1..2aaad5d 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift @@ -608,7 +608,7 @@ final class PlaybackInteractorTests: XCTestCase { } } - func test_tag_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다() async throws { + func test_tag_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다() { // Arrange let spy = PlaybackPresentationLogicSpy() sut.parentView = .tag @@ -629,19 +629,17 @@ final class PlaybackInteractorTests: XCTestCase { sut.presenter = spy // act - await sut.fetchPosts() - - try await Task.sleep(nanoseconds: 30_000_000_000) - - - // Assert - XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") - XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) - XCTAssertEqual(sut.posts?.count, 21) - XCTAssertEqual(sut.playbackVideoInfos.count, 21) + Task { + await sut.fetchPosts() + // Assert + XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") + XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) + XCTAssertEqual(sut.posts?.count, 21) + XCTAssertEqual(sut.playbackVideoInfos.count, 21) + } } - func test_Profile_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다() async throws { + func test_Profile_fetchPosts를_호출하면_presentLoadFetchVideos를_호출한다() { // Arrange let spy = PlaybackPresentationLogicSpy() sut.parentView = .otherProfile @@ -662,18 +660,17 @@ final class PlaybackInteractorTests: XCTestCase { sut.presenter = spy sut.memberID = -1 - // act - await sut.fetchPosts() - - try await Task.sleep(nanoseconds: 3_000_000_000) - - // Assert - XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") - XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) - XCTAssertEqual(sut.posts?.count, 21) + Task { + // act + await sut.fetchPosts() + // Assert + XCTAssertTrue(spy.presentLoadFetchVideosDidCalled, "fetchPosts가 presentLoadFetchVideos를 호출하지 않았습니다") + XCTAssertEqual(spy.presentLoadFetchVideosResponse.videos.count, 1) + XCTAssertEqual(sut.posts?.count, 21) + } } - func test_map이_아닐_때_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async throws { + func test_map이_아닐_때_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() { // Arrange let spy = PlaybackPresentationLogicSpy() let testPost: Post = Seeds.Posts.post1 @@ -696,22 +693,22 @@ final class PlaybackInteractorTests: XCTestCase { sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2] sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 0, boardID: 1), Models.PlaybackInfo(memberID: 0, boardID: 2)] - // act - await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 0)) - - try await Task.sleep(nanoseconds: 3_000_000_000) + Task { + // act + await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 0)) - // Assert - XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") - XCTAssertTrue(spy.presentDeleteVideoResponse.result) - XCTAssertEqual(spy.presentDeleteVideoResponse.playbackVideo, playbackVideo) - XCTAssertEqual(sut.posts!.count, 1) - XCTAssertEqual(sut.playbackVideoInfos.count, 1) + // Assert + XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") + XCTAssertTrue(spy.presentDeleteVideoResponse.result) + XCTAssertEqual(spy.presentDeleteVideoResponse.playbackVideo, playbackVideo) + XCTAssertEqual(sut.posts!.count, 1) + XCTAssertEqual(sut.playbackVideoInfos.count, 1) + } } // TODO: map paging추가되면 로직 그냥 날려버리기 - func test_map일_때_마지막_무한스크롤셀을_지울_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async throws { + func test_map일_때_마지막_무한스크롤셀을_지울_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() { // Arrange let spy = PlaybackPresentationLogicSpy() let testPost: Post = Seeds.Posts.post1 @@ -734,27 +731,26 @@ final class PlaybackInteractorTests: XCTestCase { sut.posts = [Seeds.Posts.thumbnailImageNilPost, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost ,Seeds.Posts.post1] sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1), Models.PlaybackInfo(memberID: 0, boardID: 2), Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1)] - // act - await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 3)) - - try await Task.sleep(nanoseconds: 3_000_000_000) - - - // assert - XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") - XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 1) - XCTAssertEqual(sut.playbackVideoInfos[1].boardID, 2) - XCTAssertEqual(sut.playbackVideoInfos[2].boardID, 1) - XCTAssertEqual(sut.playbackVideoInfos[3].boardID, 2) - XCTAssertEqual(sut.posts?[0].board.identifier, Seeds.Posts.post1.board.identifier) - XCTAssertEqual(sut.posts?[1].board.identifier, Seeds.Posts.post2.board.identifier) - XCTAssertEqual(sut.posts?[2].board.identifier, Seeds.Posts.post1.board.identifier) - XCTAssertEqual(sut.posts?[3].board.identifier, Seeds.Posts.post2.board.identifier) - XCTAssertEqual(sut.playbackVideoInfos.count, 4) - XCTAssertEqual(sut.posts?.count, 4) + Task { + // act + await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 3)) + + // assert + XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") + XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 1) + XCTAssertEqual(sut.playbackVideoInfos[1].boardID, 2) + XCTAssertEqual(sut.playbackVideoInfos[2].boardID, 1) + XCTAssertEqual(sut.playbackVideoInfos[3].boardID, 2) + XCTAssertEqual(sut.posts?[0].board.identifier, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(sut.posts?[1].board.identifier, Seeds.Posts.post2.board.identifier) + XCTAssertEqual(sut.posts?[2].board.identifier, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(sut.posts?[3].board.identifier, Seeds.Posts.post2.board.identifier) + XCTAssertEqual(sut.playbackVideoInfos.count, 4) + XCTAssertEqual(sut.posts?.count, 4) + } } - func test_map일_때_첫번째_무한스크롤셀을_지울_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async throws { + func test_map일_때_첫번째_무한스크롤셀을_지울_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() { // Arrange let spy = PlaybackPresentationLogicSpy() let testPost: Post = Seeds.Posts.post1 @@ -777,26 +773,26 @@ final class PlaybackInteractorTests: XCTestCase { sut.posts = [Seeds.Posts.thumbnailImageNilPost, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost ,Seeds.Posts.post1] sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1), Models.PlaybackInfo(memberID: 0, boardID: 2), Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1)] - // act - await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 1)) - - try await Task.sleep(nanoseconds: 3_000_000_000) + Task { + // act + await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 1)) + + // assert + XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") + XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 3) + XCTAssertEqual(sut.playbackVideoInfos[1].boardID, 2) + XCTAssertEqual(sut.playbackVideoInfos[2].boardID, 3) + XCTAssertEqual(sut.playbackVideoInfos[3].boardID, 2) + XCTAssertEqual(sut.posts?[0].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) + XCTAssertEqual(sut.posts?[1].board.identifier, Seeds.Posts.post2.board.identifier) + XCTAssertEqual(sut.posts?[2].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) + XCTAssertEqual(sut.posts?[3].board.identifier, Seeds.Posts.post2.board.identifier) + XCTAssertEqual(sut.playbackVideoInfos.count, 4) + XCTAssertEqual(sut.posts?.count, 4) + } + } - // assert - XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") - XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 3) - XCTAssertEqual(sut.playbackVideoInfos[1].boardID, 2) - XCTAssertEqual(sut.playbackVideoInfos[2].boardID, 3) - XCTAssertEqual(sut.playbackVideoInfos[3].boardID, 2) - XCTAssertEqual(sut.posts?[0].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) - XCTAssertEqual(sut.posts?[1].board.identifier, Seeds.Posts.post2.board.identifier) - XCTAssertEqual(sut.posts?[2].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) - XCTAssertEqual(sut.posts?[3].board.identifier, Seeds.Posts.post2.board.identifier) - XCTAssertEqual(sut.playbackVideoInfos.count, 4) - XCTAssertEqual(sut.posts?.count, 4) - } - - func test_map일_때_무한스크롤셀을_지우지않을_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() async throws { + func test_map일_때_무한스크롤셀을_지우지않을_경우_deleteVideo를_호출하면_presentDeleteVideo를_호출한다() { // Arrange let spy = PlaybackPresentationLogicSpy() let testPost: Post = Seeds.Posts.post1 @@ -819,22 +815,22 @@ final class PlaybackInteractorTests: XCTestCase { sut.posts = [Seeds.Posts.thumbnailImageNilPost, Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.thumbnailImageNilPost ,Seeds.Posts.post1] sut.playbackVideoInfos = [Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1), Models.PlaybackInfo(memberID: 0, boardID: 2), Models.PlaybackInfo(memberID: 0, boardID: 3), Models.PlaybackInfo(memberID: 0, boardID: 1)] - // act - await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 2)) - - try await Task.sleep(nanoseconds: 3_000_000_000) - - // assert - XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") - XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 3) - XCTAssertEqual(sut.playbackVideoInfos[1].boardID, 1) - XCTAssertEqual(sut.playbackVideoInfos[2].boardID, 3) - XCTAssertEqual(sut.playbackVideoInfos[3].boardID, 1) - XCTAssertEqual(sut.posts?[0].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) - XCTAssertEqual(sut.posts?[1].board.identifier, Seeds.Posts.post1.board.identifier) - XCTAssertEqual(sut.posts?[2].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) - XCTAssertEqual(sut.posts?[3].board.identifier, Seeds.Posts.post1.board.identifier) - XCTAssertEqual(sut.playbackVideoInfos.count, 4) - XCTAssertEqual(sut.posts?.count, 4) + Task { + // act + await sut.deleteVideo(with: Models.DeletePlaybackVideo.Request(playbackVideo: playbackVideo, indexPathRow: 2)) + + // assert + XCTAssertTrue(spy.presentDeleteVideoDidCalled, "deleteVideo가 presentDeleteVideo를 호출하지 않았습니다") + XCTAssertEqual(sut.playbackVideoInfos[0].boardID, 3) + XCTAssertEqual(sut.playbackVideoInfos[1].boardID, 1) + XCTAssertEqual(sut.playbackVideoInfos[2].boardID, 3) + XCTAssertEqual(sut.playbackVideoInfos[3].boardID, 1) + XCTAssertEqual(sut.posts?[0].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) + XCTAssertEqual(sut.posts?[1].board.identifier, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(sut.posts?[2].board.identifier, Seeds.Posts.thumbnailImageNilPost.board.identifier) + XCTAssertEqual(sut.posts?[3].board.identifier, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(sut.playbackVideoInfos.count, 4) + XCTAssertEqual(sut.posts?.count, 4) + } } } diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift index f9ef286..66639ce 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift @@ -46,7 +46,7 @@ final class PlaybackWorkerTests: XCTestCase { XCTAssertEqual(result[3].board.identifier, Seeds.Posts.post1.board.identifier) } - func test_deletePlaybackVideo는_올바른_동작을_반환한다() async { + func test_deletePlaybackVideo는_올바른_동작을_반환한다() { // Arrange guard let mockFileLocation = Bundle(for: type(of: self)).url(forResource: "DeleteVideo", withExtension: "json"), let mockData = try? Data(contentsOf: mockFileLocation) else { @@ -62,12 +62,14 @@ final class PlaybackWorkerTests: XCTestCase { return (response, mockData, nil) } - let response = await sut.deletePlaybackVideo(boardID: 1) + Task { + let response = await sut.deletePlaybackVideo(boardID: 1) - XCTAssertNotNil(response, "deleteVideo가 response를 정상적으로 반환하지 못함") + XCTAssertNotNil(response, "deleteVideo가 response를 정상적으로 반환하지 못함") + } } - func test_fetchHomePosts는_올바른_목록을_반환한다() async { + func test_fetchHomePosts는_올바른_목록을_반환한다() { // Arrange guard let mockFileLocation = Bundle(for: type(of: self)).url(forResource: "PostList", withExtension: "json"), let mockData = try? Data(contentsOf: mockFileLocation) else { @@ -83,25 +85,27 @@ final class PlaybackWorkerTests: XCTestCase { return (response, mockData, nil) } - // act - let result = await sut.fetchHomePosts() - - // assert - XCTAssertNotNil(result) - XCTAssertEqual(result?.count, 1, "fetchPost가 성공적으로 데이터를 받아와서 올바른 데이터 갯수를 리턴하지 못했다.") - XCTAssertEqual(result![0].tag, Seeds.Posts.post1.tag, "fetchPost가 성공적으로 데이터를 받아와서 올바른 tag를 리턴하지 못했다.") - XCTAssertEqual(result![0].board.thumbnailImageURL, Seeds.Posts.post1.board.thumbnailImageURL, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].board.identifier, Seeds.Posts.post1.board.identifier, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].board.title, Seeds.Posts.post1.board.title, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].board.description, Seeds.Posts.post1.board.description, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].board.videoURL, Seeds.Posts.post1.board.videoURL, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].board.latitude, Seeds.Posts.post1.board.latitude, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].board.longitude, Seeds.Posts.post1.board.longitude, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].board.status, Seeds.Posts.post1.board.status, "fetchPost가 성공적으로 데이터를 받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].member.identifier, Seeds.Posts.post1.member.identifier, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].member.username, Seeds.Posts.post1.member.username, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].member.introduce, Seeds.Posts.post1.member.introduce, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") - XCTAssertEqual(result![0].member.profileImageURL, Seeds.Posts.post1.member.profileImageURL, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + Task { + // act + let result = await sut.fetchHomePosts() + + // assert + XCTAssertNotNil(result) + XCTAssertEqual(result?.count, 1, "fetchPost가 성공적으로 데이터를 받아와서 올바른 데이터 갯수를 리턴하지 못했다.") + XCTAssertEqual(result![0].tag, Seeds.Posts.post1.tag, "fetchPost가 성공적으로 데이터를 받아와서 올바른 tag를 리턴하지 못했다.") + XCTAssertEqual(result![0].board.thumbnailImageURL, Seeds.Posts.post1.board.thumbnailImageURL, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.identifier, Seeds.Posts.post1.board.identifier, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.title, Seeds.Posts.post1.board.title, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.description, Seeds.Posts.post1.board.description, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.videoURL, Seeds.Posts.post1.board.videoURL, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.latitude, Seeds.Posts.post1.board.latitude, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.longitude, Seeds.Posts.post1.board.longitude, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].board.status, Seeds.Posts.post1.board.status, "fetchPost가 성공적으로 데이터를 받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].member.identifier, Seeds.Posts.post1.member.identifier, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].member.username, Seeds.Posts.post1.member.username, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].member.introduce, Seeds.Posts.post1.member.introduce, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + XCTAssertEqual(result![0].member.profileImageURL, Seeds.Posts.post1.member.profileImageURL, "fetchPost가 성공적으로 데이터를_받아와서 Post배열을 리턴하지 못했다.") + } } func test_fetchProfilePosts_는_올바른_결과를_반환한다() async { @@ -121,10 +125,12 @@ final class PlaybackWorkerTests: XCTestCase { return (response, mockData, nil) } - // act - let result = await sut.fetchProfilePosts(profileID: 2, page: 1) - - // assert - XCTAssertNotNil(result) + Task { + // act + let result = await sut.fetchProfilePosts(profileID: 2, page: 1) + + // assert + XCTAssertNotNil(result) + } } } From 4b99e0a2dd57828a39552f888f269bf94303b6f7 Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Thu, 11 Jan 2024 20:09:42 +0900 Subject: [PATCH 16/16] =?UTF-8?q?:wrench:=20Task=20=EB=B9=A0=EC=A7=84=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Workers/PlaybackInteractorTests.swift | 31 +++++++++---------- .../Mocks/Workers/PlaybackWorkerTests.swift | 2 +- .../Profile/ProfileInteractorTests.swift | 28 +++++++++-------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift index 2aaad5d..79f044a 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackInteractorTests.swift @@ -180,28 +180,27 @@ final class PlaybackInteractorTests: XCTestCase { // MARK: - Tests - func test_parentView가_map일_때_displayVideoList를_호출하면_presentVideoList를_호출하고_올바른_데이터를_전달한다() async throws { + func test_parentView가_map일_때_displayVideoList를_호출하면_presentVideoList를_호출하고_올바른_데이터를_전달한다() { // Arrange let spy = PlaybackPresentationLogicSpy() sut.presenter = spy sut.parentView = .map sut.posts = [Seeds.Posts.post1, Seeds.Posts.post2, Seeds.Posts.videoURLNilPost] + + Task { + // Act + await sut.displayVideoList() - // Act - await sut.displayVideoList() - - try await Task.sleep(nanoseconds: 3_000_000_000) - - // Assert - XCTAssertTrue(spy.presentVideoListDidCalled, "displayVideoList는 presentVideoList를 호출하지 않았습니다") - // map이므로 2 + 더미셀 2 = 4 - // videoURL이 nil이면 거름 - print(spy.presentVideoListResponse.videos) - XCTAssertEqual(spy.presentVideoListResponse.videos.count, 4) - XCTAssertEqual(spy.presentVideoListResponse.videos[0].displayedPost, spy.presentVideoListResponse.videos[2].displayedPost) - XCTAssertEqual(spy.presentVideoListResponse.videos[1].displayedPost, spy.presentVideoListResponse.videos[1].displayedPost) - XCTAssertEqual(spy.presentVideoListResponse.videos.first!.displayedPost, Seeds.PlaybackVideos.videos.last!.displayedPost) - XCTAssertEqual(spy.presentVideoListResponse.videos.last!.displayedPost, Seeds.PlaybackVideos.videos.first!.displayedPost) + // Assert + XCTAssertTrue(spy.presentVideoListDidCalled, "displayVideoList는 presentVideoList를 호출하지 않았습니다") + // map이므로 2 + 더미셀 2 = 4 + // videoURL이 nil이면 거름 + XCTAssertEqual(spy.presentVideoListResponse.videos.count, 4) + XCTAssertEqual(spy.presentVideoListResponse.videos[0].displayedPost, spy.presentVideoListResponse.videos[2].displayedPost) + XCTAssertEqual(spy.presentVideoListResponse.videos[1].displayedPost, spy.presentVideoListResponse.videos[1].displayedPost) + XCTAssertEqual(spy.presentVideoListResponse.videos.first!.displayedPost, Seeds.PlaybackVideos.videos.last!.displayedPost) + XCTAssertEqual(spy.presentVideoListResponse.videos.last!.displayedPost, Seeds.PlaybackVideos.videos.first!.displayedPost) + } } func test_displayVideoList를_호출하면_presentVideoList를_호출하고_올바른_데이터를_전달한다_parentView가_map이_아닐때() async throws { diff --git a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift index 66639ce..d9c731f 100644 --- a/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift +++ b/iOS/Layover/LayoverTests/Mocks/Workers/PlaybackWorkerTests.swift @@ -128,7 +128,7 @@ final class PlaybackWorkerTests: XCTestCase { Task { // act let result = await sut.fetchProfilePosts(profileID: 2, page: 1) - + // assert XCTAssertNotNil(result) } diff --git a/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift b/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift index aee566c..e65c41c 100644 --- a/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift @@ -66,23 +66,25 @@ final class ProfileInteractorTests: XCTestCase { // MARK: - Tests - func test_fetchProfile을_호출하면_presenter의_presentProfile을_호출하여_presentProfileResponse를_전달한다() async { + func test_fetchProfile을_호출하면_presenter의_presentProfile을_호출하여_presentProfileResponse를_전달한다() { // arrange let presentationLogicSpy = ProfilePresentationLogicSpy() sut.presenter = presentationLogicSpy - // act - await sut.fetchProfile(with: Models.FetchProfile.Request()) - - // assert - XCTAssertTrue(presentationLogicSpy.presentProfileCalled, "fetchProfile을 호출해서 presentProfile을 호출하지 못했다") - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts.count, 1, "presentProfileResponse에는 fetchProfile의 결과 갯수가 올바르게 담기지 못했다.") - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts[0].id, Seeds.Posts.post1.board.identifier, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts[0].thumbnailImageData, Seeds.sampleImageData, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts[0].status, Seeds.Posts.post1.board.status, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.username, Seeds.Members.getMember1.username, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.introduce, Seeds.Members.getMember1.introduce, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.profileImageData, Seeds.sampleImageData, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") + Task { + // act + await sut.fetchProfile(with: Models.FetchProfile.Request()) + + // assert + XCTAssertTrue(presentationLogicSpy.presentProfileCalled, "fetchProfile을 호출해서 presentProfile을 호출하지 못했다") + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts.count, 1, "presentProfileResponse에는 fetchProfile의 결과 갯수가 올바르게 담기지 못했다.") + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts[0].id, Seeds.Posts.post1.board.identifier, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts[0].thumbnailImageData, Seeds.sampleImageData, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts[0].status, Seeds.Posts.post1.board.status, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.username, Seeds.Members.getMember1.username, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.introduce, Seeds.Members.getMember1.introduce, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.profileImageData, Seeds.sampleImageData, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담기지 못했다.") + } } func test_fetchMorePosts을_호출하면_presenter의_presentMorePosts을_호출하고_presentMorePostsResponse를_전달한다() async {