mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
added tests
This commit is contained in:
@@ -851,6 +851,7 @@
|
||||
9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; };
|
||||
92720AB0DA9AB5EEF1DAF56B /* SecureBackupLogoutConfirmationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC017C3CB6B0F7C63F460F2 /* SecureBackupLogoutConfirmationScreenViewModel.swift */; };
|
||||
9278EC51D24E57445B290521 /* AudioSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB284643AF7AB131E307DCE0 /* AudioSessionProtocol.swift */; };
|
||||
92919B6522B26B2681232EAC /* LiveLocationManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B5DF0E888F66652F8C4CEC5 /* LiveLocationManagerMock.swift */; };
|
||||
9295F1F5E04484E10780BCE8 /* CharacterSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F8C01DEEA83903D45069BBD /* CharacterSet.swift */; };
|
||||
92A0309B6F490B86C10B603B /* accountsV2 in Resources */ = {isa = PBXBuildFile; fileRef = 2A7BE2B89310058659E6F459 /* accountsV2 */; };
|
||||
92D9088B901CEBB1A99ECA4E /* RoomMemberProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */; };
|
||||
@@ -1636,6 +1637,7 @@
|
||||
0AE4B7AD74E402BBC46A801A /* LeaveSpaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeaveSpaceView.swift; sourceTree = "<group>"; };
|
||||
0B0E0B55E2EE75AF67029924 /* SwipeToReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToReplyView.swift; sourceTree = "<group>"; };
|
||||
0B32BBA8887BD7A5C4ECF16F /* RoomModerationRole.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomModerationRole.swift; sourceTree = "<group>"; };
|
||||
0B5DF0E888F66652F8C4CEC5 /* LiveLocationManagerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveLocationManagerMock.swift; sourceTree = "<group>"; };
|
||||
0B987FC3FDBAA0E1C5AA235C /* PaginationIndicatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationIndicatorRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
0BA7D6C94A50428463D09AF0 /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
0BB05221D7D941CC82DC8480 /* LogViewerScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewerScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
@@ -3697,6 +3699,7 @@
|
||||
9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */,
|
||||
7F957320D0EB7D7B4E30C79D /* KnockRequestProxyMock.swift */,
|
||||
BA257D747DD7E6FFA5C2BE2D /* LinkNewDeviceServiceMock.swift */,
|
||||
0B5DF0E888F66652F8C4CEC5 /* LiveLocationManagerMock.swift */,
|
||||
6F65E4BB9E82EB8373207CF8 /* MediaProviderMock.swift */,
|
||||
8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */,
|
||||
840182D7A61402D5947DE094 /* NotificationItemProxyMock.swift */,
|
||||
@@ -8308,6 +8311,7 @@
|
||||
74DF5BC17DE9F51E077FD457 /* LinkNewDeviceServiceMock.swift in Sources */,
|
||||
866FA35E7A2339EF8B6D91CA /* LinkPreviewView.swift in Sources */,
|
||||
03364DE2568860586A713472 /* LiveLocationManager.swift in Sources */,
|
||||
92919B6522B26B2681232EAC /* LiveLocationManagerMock.swift in Sources */,
|
||||
CD077E14FAADC444C5A80068 /* LiveLocationManagerProtocol.swift in Sources */,
|
||||
C8D0AC22E03F652118A2BB73 /* LiveLocationRoomTimelineItem.swift in Sources */,
|
||||
F7977C53B2B1D73030C69761 /* LiveLocationRoomTimelineView.swift in Sources */,
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import CoreLocation
|
||||
|
||||
extension LiveLocationManagerMock {
|
||||
struct Configuration {
|
||||
var authorizationStatus: CLAuthorizationStatus = .notDetermined
|
||||
var requestAlwaysAuthorizationIfPossibleReturnValue = true
|
||||
}
|
||||
|
||||
convenience init(_ configuration: Configuration) {
|
||||
self.init()
|
||||
|
||||
let authorizationStatusSubject = CurrentValueSubject<CLAuthorizationStatus, Never>(configuration.authorizationStatus)
|
||||
underlyingAuthorizationStatus = .init(authorizationStatusSubject)
|
||||
|
||||
requestAlwaysAuthorizationIfPossibleReturnValue = configuration.requestAlwaysAuthorizationIfPossibleReturnValue
|
||||
}
|
||||
}
|
||||
@@ -7,34 +7,26 @@
|
||||
//
|
||||
|
||||
import Combine
|
||||
import CoreLocation
|
||||
@testable import ElementX
|
||||
import Testing
|
||||
|
||||
@MainActor
|
||||
struct LocationSharingScreenViewModelTests {
|
||||
private let timelineProxy = TimelineProxyMock(.init())
|
||||
private var viewModel: LocationSharingScreenViewModelProtocol
|
||||
var timelineProxy: TimelineProxyMock!
|
||||
var viewModel: LocationSharingScreenViewModel!
|
||||
|
||||
private var context: LocationSharingScreenViewModel.Context {
|
||||
var context: LocationSharingScreenViewModel.Context {
|
||||
viewModel.context
|
||||
}
|
||||
|
||||
init() {
|
||||
let viewModel = LocationSharingScreenViewModel(interactionMode: .picker,
|
||||
mapURLBuilder: ServiceLocator.shared.settings.mapTilerConfiguration,
|
||||
liveLocationSharingEnabled: true,
|
||||
roomProxy: JoinedRoomProxyMock(.init()),
|
||||
timelineController: MockTimelineController(timelineProxy: timelineProxy),
|
||||
liveLocationManager: LiveLocationManagerMock(),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||
viewModel.state.bindings.isLocationAuthorized = true
|
||||
self.viewModel = viewModel
|
||||
AppSettings.resetAllSettings()
|
||||
}
|
||||
|
||||
@Test
|
||||
func userDidPan() {
|
||||
mutating func userDidPan() {
|
||||
setupViewModel()
|
||||
#expect(context.viewState.isSharingUserLocation)
|
||||
#expect(context.showsUserLocationMode == .showAndFollow)
|
||||
context.send(viewAction: .userDidPan)
|
||||
@@ -43,7 +35,8 @@ struct LocationSharingScreenViewModelTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func centerOnUser() {
|
||||
mutating func centerOnUser() {
|
||||
setupViewModel()
|
||||
#expect(context.viewState.isSharingUserLocation)
|
||||
context.showsUserLocationMode = .show
|
||||
#expect(!context.viewState.isSharingUserLocation)
|
||||
@@ -53,7 +46,8 @@ struct LocationSharingScreenViewModelTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func centerOnUserWithoutAuth() {
|
||||
mutating func centerOnUserWithoutAuth() {
|
||||
setupViewModel()
|
||||
context.showsUserLocationMode = .hide
|
||||
context.isLocationAuthorized = nil
|
||||
context.send(viewAction: .centerToUser)
|
||||
@@ -61,7 +55,8 @@ struct LocationSharingScreenViewModelTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func centerOnUserWithDeniedAuth() {
|
||||
mutating func centerOnUserWithDeniedAuth() {
|
||||
setupViewModel()
|
||||
context.isLocationAuthorized = false
|
||||
context.showsUserLocationMode = .hide
|
||||
context.send(viewAction: .centerToUser)
|
||||
@@ -70,7 +65,8 @@ struct LocationSharingScreenViewModelTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func errorMapping() {
|
||||
mutating func errorMapping() {
|
||||
setupViewModel()
|
||||
let mapError = AlertInfo(locationSharingViewError: .mapError(.failedLoadingMap))
|
||||
#expect(mapError.title == L10n.errorFailedLoadingMap(InfoPlistReader.main.bundleDisplayName))
|
||||
let locationError = AlertInfo(locationSharingViewError: .mapError(.failedLocatingUser))
|
||||
@@ -80,7 +76,8 @@ struct LocationSharingScreenViewModelTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func sendUserLocation() async throws {
|
||||
mutating func sendUserLocation() async throws {
|
||||
setupViewModel()
|
||||
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
||||
context.geolocationUncertainty = 10
|
||||
|
||||
@@ -101,7 +98,8 @@ struct LocationSharingScreenViewModelTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func sendPickedLocation() async throws {
|
||||
mutating func sendPickedLocation() async throws {
|
||||
setupViewModel()
|
||||
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
||||
context.isLocationAuthorized = nil
|
||||
context.geolocationUncertainty = 10
|
||||
@@ -121,4 +119,101 @@ struct LocationSharingScreenViewModelTests {
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Live Location Authorization Tests
|
||||
|
||||
@Test
|
||||
mutating func startLiveLocationWithDeniedAuth() {
|
||||
setupViewModel(liveLocationManagerConfiguration: .init(authorizationStatus: .denied))
|
||||
context.send(viewAction: .startLiveLocation)
|
||||
#expect(context.alertInfo?.id == .missingAlwaysAuthorization)
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func startLiveLocationWithRestrictedAuth() {
|
||||
setupViewModel(liveLocationManagerConfiguration: .init(authorizationStatus: .restricted))
|
||||
context.send(viewAction: .startLiveLocation)
|
||||
#expect(context.alertInfo?.id == .missingAlwaysAuthorization)
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func startLiveLocationWithAlwaysAuth() {
|
||||
setupViewModel(liveLocationManagerConfiguration: .init(authorizationStatus: .authorizedAlways))
|
||||
context.send(viewAction: .startLiveLocation)
|
||||
#expect(context.alertInfo == nil)
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func startLiveLocationWithWhenInUseAuthAlreadyRequested() {
|
||||
setupViewModel(liveLocationManagerConfiguration: .init(authorizationStatus: .authorizedWhenInUse,
|
||||
requestAlwaysAuthorizationIfPossibleReturnValue: false))
|
||||
context.send(viewAction: .startLiveLocation)
|
||||
#expect(context.alertInfo?.id == .missingAlwaysAuthorization)
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func startLiveLocationWithWhenInUseAuthNotYetRequested() {
|
||||
setupViewModel(liveLocationManagerConfiguration: .init(authorizationStatus: .authorizedWhenInUse,
|
||||
requestAlwaysAuthorizationIfPossibleReturnValue: true))
|
||||
context.send(viewAction: .startLiveLocation)
|
||||
// The request can be made, so no alert should be shown — it waits for the user to respond to the system prompt
|
||||
#expect(context.alertInfo == nil)
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func startLiveLocationWithNotDeterminedAuthTransitionsToWhenInUse() async {
|
||||
let authorizationStatusSubject = CurrentValueSubject<CLAuthorizationStatus, Never>(.notDetermined)
|
||||
let liveLocationManagerMock = LiveLocationManagerMock()
|
||||
liveLocationManagerMock.underlyingAuthorizationStatus = .init(authorizationStatusSubject)
|
||||
liveLocationManagerMock.requestAlwaysAuthorizationIfPossibleReturnValue = true
|
||||
setupViewModel(liveLocationManagerMock: liveLocationManagerMock)
|
||||
|
||||
context.send(viewAction: .startLiveLocation)
|
||||
|
||||
// No alert yet — waiting for MapLibre to resolve the status to whenInUse
|
||||
#expect(context.alertInfo == nil)
|
||||
|
||||
// Simulate MapLibre resolving the authorization to whenInUse, and confirm that the ViewModel
|
||||
// recurses and calls requestAlwaysAuthorizationIfPossible as a result
|
||||
await waitForConfirmation { confirmation in
|
||||
liveLocationManagerMock.requestAlwaysAuthorizationIfPossibleClosure = {
|
||||
confirmation()
|
||||
return true
|
||||
}
|
||||
authorizationStatusSubject.send(.authorizedWhenInUse)
|
||||
}
|
||||
|
||||
// The request was made, so no alert — waiting for the always authorization prompt response
|
||||
#expect(context.alertInfo == nil)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private mutating func setupViewModel(liveLocationManagerConfiguration: LiveLocationManagerMock.Configuration = .init()) {
|
||||
timelineProxy = TimelineProxyMock(.init())
|
||||
viewModel = LocationSharingScreenViewModel(interactionMode: .picker,
|
||||
mapURLBuilder: ServiceLocator.shared.settings.mapTilerConfiguration,
|
||||
liveLocationSharingEnabled: true,
|
||||
roomProxy: JoinedRoomProxyMock(.init()),
|
||||
timelineController: MockTimelineController(timelineProxy: timelineProxy),
|
||||
liveLocationManager: LiveLocationManagerMock(liveLocationManagerConfiguration),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||
viewModel.state.bindings.isLocationAuthorized = true
|
||||
}
|
||||
|
||||
private mutating func setupViewModel(liveLocationManagerMock: LiveLocationManagerMock) {
|
||||
timelineProxy = TimelineProxyMock(.init())
|
||||
viewModel = LocationSharingScreenViewModel(interactionMode: .picker,
|
||||
mapURLBuilder: ServiceLocator.shared.settings.mapTilerConfiguration,
|
||||
liveLocationSharingEnabled: true,
|
||||
roomProxy: JoinedRoomProxyMock(.init()),
|
||||
timelineController: MockTimelineController(timelineProxy: timelineProxy),
|
||||
liveLocationManager: liveLocationManagerMock,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||
viewModel.state.bindings.isLocationAuthorized = true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user