mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
ff5dcb040d
Removes the ServiceLocator singleton and instead directly injects the dependencies it was holding for more control over instantiation of the various component subtrees and better visibility over how they're being used.
222 lines
9.3 KiB
Swift
222 lines
9.3 KiB
Swift
//
|
|
// Copyright 2025 Element Creations Ltd.
|
|
// Copyright 2022-2025 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
// Please see LICENSE files in the repository root for full details.
|
|
//
|
|
|
|
@testable import ElementX
|
|
import Testing
|
|
|
|
@MainActor
|
|
struct RoomMemberDetailsViewModelTests {
|
|
var clientProxy: ClientProxy!
|
|
var viewModel: RoomMemberDetailsScreenViewModelProtocol!
|
|
var roomProxyMock: JoinedRoomProxyMock!
|
|
var roomMemberProxyMock: RoomMemberProxyMock!
|
|
var context: RoomMemberDetailsScreenViewModelType.Context {
|
|
viewModel.context
|
|
}
|
|
|
|
@Test
|
|
mutating func initialState() async throws {
|
|
setup(roomMemberProxyMock: .mockAlice)
|
|
|
|
let waitForMemberToLoad = deferFulfillment(context.$viewState) { $0.memberDetails != nil }
|
|
try await waitForMemberToLoad.fulfill()
|
|
|
|
#expect(context.viewState.memberDetails == RoomMemberDetails(withProxy: roomMemberProxyMock))
|
|
#expect(context.ignoreUserAlert == nil)
|
|
#expect(context.alertInfo == nil)
|
|
}
|
|
|
|
@Test
|
|
mutating func ignoreSuccess() async throws {
|
|
setup(roomMemberProxyMock: .mockAlice)
|
|
|
|
let waitForMemberToLoad = deferFulfillment(context.$viewState) { $0.memberDetails != nil }
|
|
try await waitForMemberToLoad.fulfill()
|
|
|
|
context.send(viewAction: .showIgnoreAlert)
|
|
#expect(context.ignoreUserAlert == .init(action: .ignore))
|
|
context.send(viewAction: .ignoreConfirmed)
|
|
let deferred = deferFulfillment(context.$viewState) { state in
|
|
state.memberDetails?.isIgnored == true
|
|
}
|
|
try await deferred.fulfill()
|
|
|
|
let memberDetails = try #require(context.viewState.memberDetails,
|
|
"Member details should be loaded at this point")
|
|
#expect(memberDetails.isIgnored)
|
|
#expect(!context.viewState.isProcessingIgnoreRequest)
|
|
try await Task.sleep(for: .milliseconds(100))
|
|
#expect(roomProxyMock.updateMembersCalled)
|
|
}
|
|
|
|
@Test
|
|
mutating func ignoreFailure() async throws {
|
|
let clientProxy = ClientProxyMock(.init())
|
|
clientProxy.ignoreUserReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
|
|
setup(roomMemberProxyMock: .mockAlice, clientProxy: clientProxy)
|
|
|
|
let waitForMemberToLoad = deferFulfillment(context.$viewState) { $0.memberDetails != nil }
|
|
try await waitForMemberToLoad.fulfill()
|
|
|
|
context.send(viewAction: .showIgnoreAlert)
|
|
#expect(context.ignoreUserAlert == .init(action: .ignore))
|
|
context.send(viewAction: .ignoreConfirmed)
|
|
let deferred = deferFulfillment(context.$viewState) { state in
|
|
state.bindings.alertInfo != nil
|
|
}
|
|
try await deferred.fulfill()
|
|
|
|
let memberDetails = try #require(context.viewState.memberDetails,
|
|
"Member details should be loaded at this point")
|
|
#expect(!memberDetails.isIgnored)
|
|
#expect(context.alertInfo != nil)
|
|
|
|
try await Task.sleep(for: .milliseconds(100))
|
|
#expect(!roomProxyMock.updateMembersCalled)
|
|
}
|
|
|
|
@Test
|
|
mutating func unignoreSuccess() async throws {
|
|
setup(roomMemberProxyMock: .mockIgnored)
|
|
|
|
let waitForMemberToLoad = deferFulfillment(context.$viewState) { $0.memberDetails != nil }
|
|
try await waitForMemberToLoad.fulfill()
|
|
|
|
context.send(viewAction: .showUnignoreAlert)
|
|
#expect(context.ignoreUserAlert == .init(action: .unignore))
|
|
context.send(viewAction: .unignoreConfirmed)
|
|
let deferred = deferFulfillment(context.$viewState) { state in
|
|
state.memberDetails?.isIgnored == false
|
|
}
|
|
try await deferred.fulfill()
|
|
|
|
let memberDetails = try #require(context.viewState.memberDetails,
|
|
"Member details should be loaded at this point")
|
|
#expect(!memberDetails.isIgnored)
|
|
|
|
try await Task.sleep(for: .milliseconds(100))
|
|
#expect(roomProxyMock.updateMembersCalled)
|
|
}
|
|
|
|
@Test
|
|
mutating func unignoreFailure() async throws {
|
|
let clientProxy = ClientProxyMock(.init())
|
|
clientProxy.unignoreUserReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
|
|
setup(roomMemberProxyMock: .mockIgnored, clientProxy: clientProxy)
|
|
|
|
let waitForMemberToLoad = deferFulfillment(context.$viewState) { $0.memberDetails != nil }
|
|
try await waitForMemberToLoad.fulfill()
|
|
|
|
context.send(viewAction: .showUnignoreAlert)
|
|
#expect(context.ignoreUserAlert == .init(action: .unignore))
|
|
context.send(viewAction: .unignoreConfirmed)
|
|
let deferred = deferFulfillment(context.$viewState) { state in
|
|
state.bindings.alertInfo != nil
|
|
}
|
|
try await deferred.fulfill()
|
|
|
|
let memberDetails = try #require(context.viewState.memberDetails,
|
|
"Member details should be loaded at this point")
|
|
#expect(memberDetails.isIgnored)
|
|
#expect(context.alertInfo != nil)
|
|
|
|
try await Task.sleep(for: .milliseconds(100))
|
|
#expect(!roomProxyMock.updateMembersCalled)
|
|
}
|
|
|
|
@Test
|
|
mutating func initialStateAccountOwner() async throws {
|
|
setup(roomMemberProxyMock: .mockMe)
|
|
|
|
let waitForMemberToLoad = deferFulfillment(context.$viewState) { $0.memberDetails != nil }
|
|
try await waitForMemberToLoad.fulfill()
|
|
|
|
#expect(context.viewState.memberDetails == RoomMemberDetails(withProxy: roomMemberProxyMock))
|
|
#expect(context.ignoreUserAlert == nil)
|
|
#expect(context.alertInfo == nil)
|
|
}
|
|
|
|
@Test
|
|
mutating func initialStateIgnoredUser() async throws {
|
|
setup(roomMemberProxyMock: .mockIgnored)
|
|
|
|
let waitForMemberToLoad = deferFulfillment(context.$viewState) { $0.memberDetails != nil }
|
|
try await waitForMemberToLoad.fulfill()
|
|
|
|
#expect(context.viewState.memberDetails == RoomMemberDetails(withProxy: roomMemberProxyMock))
|
|
#expect(context.ignoreUserAlert == nil)
|
|
#expect(context.alertInfo == nil)
|
|
}
|
|
|
|
// MARK: - History Sharing
|
|
|
|
@Test
|
|
mutating func inviteConfirmationFetchesIdentity() async throws {
|
|
let clientProxy = ClientProxyMock(.init())
|
|
setup(roomMemberProxyMock: .mockBob, clientProxy: clientProxy)
|
|
|
|
let waitForMemberToLoad = deferFulfillment(context.$viewState) { $0.memberDetails != nil }
|
|
try await waitForMemberToLoad.fulfill()
|
|
|
|
clientProxy.directRoomForUserIDReturnValue = .success(nil)
|
|
clientProxy.userIdentityForFallBackToServerReturnValue = .success(UserIdentityProxyMock(configuration: .init(verificationState: .notVerified)))
|
|
|
|
// The user identity becomes known, i.e. not unknown.
|
|
let deferred = deferFulfillment(viewModel.context.$viewState.compactMap(\.bindings.inviteConfirmationUser)) {
|
|
!$0.isUnknown
|
|
}
|
|
context.send(viewAction: .openDirectChat)
|
|
try await deferred.fulfill()
|
|
|
|
#expect(clientProxy.userIdentityForFallBackToServerCalled)
|
|
}
|
|
|
|
@Test
|
|
mutating func inviteConfirmationFallsBackToUnknownIdentityOnFailure() async throws {
|
|
let clientProxy = ClientProxyMock(.init())
|
|
setup(roomMemberProxyMock: .mockBob, clientProxy: clientProxy)
|
|
|
|
let waitForMemberToLoad = deferFulfillment(context.$viewState) { $0.memberDetails != nil }
|
|
try await waitForMemberToLoad.fulfill()
|
|
|
|
clientProxy.directRoomForUserIDReturnValue = .success(nil)
|
|
clientProxy.userIdentityForFallBackToServerReturnValue = .failure(.forbiddenAccess)
|
|
|
|
// The user identity is always unknown, i.e. never not unknown.
|
|
let deferred = deferFailure(viewModel.context.$viewState.compactMap(\.bindings.inviteConfirmationUser), timeout: .seconds(5)) {
|
|
!$0.isUnknown
|
|
}
|
|
context.send(viewAction: .openDirectChat)
|
|
try await deferred.fulfill()
|
|
|
|
#expect(clientProxy.userIdentityForFallBackToServerCalled)
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private mutating func setup(roomMemberProxyMock: RoomMemberProxyMock, clientProxy: ClientProxyMock? = nil) {
|
|
self.roomMemberProxyMock = roomMemberProxyMock
|
|
roomProxyMock = JoinedRoomProxyMock(.init(name: ""))
|
|
roomProxyMock.getMemberUserIDClosure = { _ in
|
|
.success(roomMemberProxyMock)
|
|
}
|
|
|
|
let appSettings = AppSettings()
|
|
let analytics = AnalyticsService.mock(settings: appSettings)
|
|
|
|
// swiftlint:disable:next force_unwrapping
|
|
let userSession = clientProxy != nil ? UserSessionMock(.init(clientProxy: clientProxy!)) : UserSessionMock(.init())
|
|
viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID,
|
|
roomProxy: roomProxyMock,
|
|
userSession: userSession,
|
|
userIndicatorController: UserIndicatorControllerMock.default,
|
|
analytics: analytics,
|
|
appSettings: appSettings)
|
|
}
|
|
}
|