From e4da266c93679f0568728fd42599a8a48094d1b7 Mon Sep 17 00:00:00 2001 From: swift-man Date: Sun, 17 May 2026 09:03:23 +0900 Subject: [PATCH] =?UTF-8?q?test:=20=EC=B0=A8=EB=8B=A8=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=20=EB=A6=AC=EB=B7=B0=20=ED=9B=84=EC=86=8D=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR 리뷰에서 제안된 BlockingLaunchView 옵셔널 바인딩을 적용했습니다. pending fetch 흐름은 TestStore에서 후속 fetch effect와 stubbed LaunchingService 응답까지 검증하도록 보강했습니다. --- .../Private/BlockingLaunchView.swift | 2 +- .../LaunchingViewTests.swift | 63 +++++++++++++------ 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/Sources/LaunchingView/Private/BlockingLaunchView.swift b/Sources/LaunchingView/Private/BlockingLaunchView.swift index 2e2a5f4..65cbfb8 100644 --- a/Sources/LaunchingView/Private/BlockingLaunchView.swift +++ b/Sources/LaunchingView/Private/BlockingLaunchView.swift @@ -40,7 +40,7 @@ struct BlockingLaunchView: View { } } - if linkURL != nil { + if let linkURL { Button { onButtonTapped(linkURL) } label: { diff --git a/Tests/LaunchingViewTests/LaunchingViewTests.swift b/Tests/LaunchingViewTests/LaunchingViewTests.swift index 18fb75c..0056162 100644 --- a/Tests/LaunchingViewTests/LaunchingViewTests.swift +++ b/Tests/LaunchingViewTests/LaunchingViewTests.swift @@ -182,7 +182,7 @@ struct LaunchingViewTests { } @Test - func fetchWhileFetchingMarksPendingRefresh() { + func fetchWhileFetchingRefreshesAgainAfterCurrentStatusFinishes() async { let url = URL(string: "https://example.com/force")! let forceStatus = AppUpdateStatus.forcedUpdateRequired( UpdateAlert( @@ -191,33 +191,56 @@ struct LaunchingViewTests { alertDoneLinkURL: url ) ) - var state = Launching.State(isFetching: true) - let reducer = Launching() - - _ = reducer.reduce(into: &state, action: .fetchAppUpdateStatus) - - #expect(state.hasPendingFetch) + let store = TestStore( + initialState: Launching.State(isFetching: true), + reducer: Launching() + ) + store.dependencies.launchingAlertDefaultText = LaunchingAlertDefaultText( + forceUpdate: .init(done: "Update") + ) + store.dependencies.launchingService = StubLaunchingService(status: .valid) - withDependencies { - $0.launchingAlertDefaultText = LaunchingAlertDefaultText( - forceUpdate: .init(done: "Update") - ) - } operation: { - _ = reducer.reduce(into: &state, action: .setAppUpdateStatus(forceStatus)) + await store.send(.fetchAppUpdateStatus) { + $0.hasPendingFetch = true } - #expect(!state.isFetching) - #expect(!state.hasPendingFetch) - #expect(state.appUpdateStatus == forceStatus) - #expect(state.displayContentView == false) - #expect( - state.blockingAlert == Launching.State.BlockingAlert( + await store.send(.setAppUpdateStatus(forceStatus)) { + $0.isFetching = false + $0.hasPendingFetch = false + $0.appUpdateStatus = forceStatus + $0.displayContentView = false + $0.blockingAlert = Launching.State.BlockingAlert( title: "Force update", message: "Please update now", buttonTitle: "Update", linkURL: url ) - ) + } + + await store.receive(.fetchAppUpdateStatus) { + $0.isFetching = true + } + + await store.receive(.setAppUpdateStatus(.valid)) { + $0.isFetching = false + $0.appUpdateStatus = .valid + $0.displayContentView = true + $0.blockingAlert = nil + $0.optionalUpdateAlert = nil + $0.noticeAlert = nil + } + } +} + +private final class StubLaunchingService: LaunchingInteractable { + private let status: AppUpdateStatus + + init(status: AppUpdateStatus) { + self.status = status + } + + func fetchAppUpdateStatus() async throws -> AppUpdateStatus { + status } }