mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Use deferFulfillment in flow coordinator tests (#5861)
* use deferFulfillment in coordinator tests * pr suggestions
This commit is contained in:
@@ -173,10 +173,12 @@ struct ChatsTabFlowCoordinatorTests {
|
||||
chatsTabFlowCoordinator.handleAppRoute(.roomDetails(roomID: "1"), animated: true)
|
||||
try await unexpectedFulfillment.fulfill()
|
||||
|
||||
#expect(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
#expect(detailNavigationStack?.stackCoordinators.count == 1)
|
||||
#expect(detailNavigationStack?.stackCoordinators.first is RoomDetailsScreenCoordinator)
|
||||
#expect(detailCoordinator != nil)
|
||||
let detailStack = try #require(detailNavigationStack)
|
||||
let deferredPush = deferFulfillment(detailStack.observe(\.stackCoordinators.count)) { $0 == 1 }
|
||||
try await deferredPush.fulfill()
|
||||
|
||||
#expect(detailStack.rootCoordinator is RoomScreenCoordinator)
|
||||
#expect(detailStack.stackCoordinators.first is RoomDetailsScreenCoordinator)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -211,11 +213,12 @@ struct ChatsTabFlowCoordinatorTests {
|
||||
#expect(detailCoordinator != nil)
|
||||
|
||||
chatsTabFlowCoordinator.handleAppRoute(.childRoom(roomID: "2", via: []), animated: true)
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
#expect(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
#expect(detailNavigationStack?.stackCoordinators.count == 1)
|
||||
#expect(detailNavigationStack?.stackCoordinators.first is RoomScreenCoordinator)
|
||||
#expect(detailCoordinator != nil)
|
||||
let detailStack = try #require(detailNavigationStack)
|
||||
let deferredPush = deferFulfillment(detailStack.observe(\.stackCoordinators.count)) { $0 == 1 }
|
||||
try await deferredPush.fulfill()
|
||||
|
||||
#expect(detailStack.rootCoordinator is RoomScreenCoordinator)
|
||||
#expect(detailStack.stackCoordinators.first is RoomScreenCoordinator)
|
||||
|
||||
try await process(route: .room(roomID: "3", via: []), expectedState: .roomList(detailState: .room(roomID: "3")))
|
||||
#expect(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
@@ -235,11 +238,13 @@ struct ChatsTabFlowCoordinatorTests {
|
||||
|
||||
// A child event route should push a new room screen onto the stack and focus on the event.
|
||||
chatsTabFlowCoordinator.handleAppRoute(.childEvent(eventID: "2", roomID: "2", via: []), animated: true)
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
#expect(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
#expect(detailNavigationStack?.stackCoordinators.count == 1)
|
||||
#expect(detailNavigationStack?.stackCoordinators.first is RoomScreenCoordinator)
|
||||
#expect(detailCoordinator != nil)
|
||||
let detailStack = try #require(detailNavigationStack)
|
||||
let deferredPush = deferFulfillment(detailStack.observe(\.stackCoordinators.count)) { $0 == 1 }
|
||||
try await deferredPush.fulfill()
|
||||
|
||||
#expect(detailStack.rootCoordinator is RoomScreenCoordinator)
|
||||
#expect(detailStack.stackCoordinators.count == 1)
|
||||
#expect(detailStack.stackCoordinators.first is RoomScreenCoordinator)
|
||||
#expect(timelineControllerFactory.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount == 2)
|
||||
#expect(timelineControllerFactory.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID == "2")
|
||||
|
||||
@@ -270,6 +275,9 @@ struct ChatsTabFlowCoordinatorTests {
|
||||
try await process(route: .share(sharePayload),
|
||||
expectedState: .roomList(detailState: .room(roomID: "2")))
|
||||
|
||||
let deferredSheet = deferFulfillment(splitCoordinator.observe(\.sheetCoordinatorID)) { $0 != nil }
|
||||
try await deferredSheet.fulfill()
|
||||
|
||||
#expect(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
#expect((splitCoordinator.sheetCoordinator as? NavigationStackCoordinator)?.rootCoordinator is MediaUploadPreviewScreenCoordinator)
|
||||
}
|
||||
@@ -289,12 +297,57 @@ struct ChatsTabFlowCoordinatorTests {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private mutating func process(route: AppRoute, expectedState: ChatsTabFlowCoordinatorStateMachine.State) async throws {
|
||||
// Sometimes the state machine's state changes before the coordinators have updated the stack.
|
||||
let delayedPublisher = stateMachineFactory.chatsTabFlowStatePublisher.delay(for: .milliseconds(100), scheduler: DispatchQueue.main)
|
||||
private func process(route: AppRoute, expectedState: ChatsTabFlowCoordinatorStateMachine.State) async throws {
|
||||
// Keep the previous root coordinator alive while waiting, otherwise a newly presented
|
||||
// coordinator could be allocated at the same address and be mistaken for it below.
|
||||
let previousDetailRootCoordinator = splitCoordinator.detailRootCoordinator
|
||||
let previousDetailRootCoordinatorID = previousDetailRootCoordinator.map { ObjectIdentifier($0) }
|
||||
|
||||
let deferred = deferFulfillment(delayedPublisher) { $0 == expectedState }
|
||||
let deferred = deferFulfillment(stateMachineFactory.chatsTabFlowStatePublisher) { $0 == expectedState }
|
||||
chatsTabFlowCoordinator.handleAppRoute(route, animated: true)
|
||||
try await deferred.fulfill()
|
||||
|
||||
// The state machine's state changes before the coordinators have updated the stack,
|
||||
// so also wait for the navigation side effect implied by the expected state.
|
||||
switch expectedState {
|
||||
case .roomList(detailState: .some(.room)):
|
||||
let deferredDetail = deferFulfillment(splitCoordinator.observe(\.detailRootCoordinatorID)) { $0 != nil && $0 != previousDetailRootCoordinatorID }
|
||||
try await deferredDetail.fulfill()
|
||||
case .roomList(detailState: nil):
|
||||
let deferredDetail = deferFulfillment(splitCoordinator.observe(\.detailCoordinatorID)) { $0 == nil }
|
||||
try await deferredDetail.fulfill()
|
||||
case .userProfileScreen:
|
||||
let deferredDetail = deferFulfillment(splitCoordinator.observe(\.detailRootCoordinatorID)) { $0 == nil }
|
||||
try await deferredDetail.fulfill()
|
||||
|
||||
let deferredSheet = deferFulfillment(splitCoordinator.observe(\.sheetCoordinatorID)) { $0 != nil }
|
||||
try await deferredSheet.fulfill()
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
withExtendedLifetime(previousDetailRootCoordinator) { }
|
||||
}
|
||||
}
|
||||
|
||||
// Sendable projections of the split coordinator's non-Sendable coordinator references,
|
||||
// allowing the tests to `observe` them and wait for navigation with `deferFulfillment`.
|
||||
private extension NavigationSplitCoordinator {
|
||||
var detailCoordinatorID: ObjectIdentifier? {
|
||||
detailCoordinator.map { ObjectIdentifier($0) }
|
||||
}
|
||||
|
||||
/// The root coordinator inside the detail navigation stack.
|
||||
var detailRootCoordinator: (any CoordinatorProtocol)? {
|
||||
(detailCoordinator as? NavigationStackCoordinator)?.rootCoordinator
|
||||
}
|
||||
|
||||
/// The identity of the root coordinator inside the detail navigation stack.
|
||||
var detailRootCoordinatorID: ObjectIdentifier? {
|
||||
detailRootCoordinator.map { ObjectIdentifier($0) }
|
||||
}
|
||||
|
||||
var sheetCoordinatorID: ObjectIdentifier? {
|
||||
sheetCoordinator.map { ObjectIdentifier($0) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user