mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
added tests
This commit is contained in:
@@ -851,6 +851,7 @@
|
|||||||
9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; };
|
9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; };
|
||||||
92720AB0DA9AB5EEF1DAF56B /* SecureBackupLogoutConfirmationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC017C3CB6B0F7C63F460F2 /* SecureBackupLogoutConfirmationScreenViewModel.swift */; };
|
92720AB0DA9AB5EEF1DAF56B /* SecureBackupLogoutConfirmationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC017C3CB6B0F7C63F460F2 /* SecureBackupLogoutConfirmationScreenViewModel.swift */; };
|
||||||
9278EC51D24E57445B290521 /* AudioSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB284643AF7AB131E307DCE0 /* AudioSessionProtocol.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 */; };
|
9295F1F5E04484E10780BCE8 /* CharacterSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F8C01DEEA83903D45069BBD /* CharacterSet.swift */; };
|
||||||
92A0309B6F490B86C10B603B /* accountsV2 in Resources */ = {isa = PBXBuildFile; fileRef = 2A7BE2B89310058659E6F459 /* accountsV2 */; };
|
92A0309B6F490B86C10B603B /* accountsV2 in Resources */ = {isa = PBXBuildFile; fileRef = 2A7BE2B89310058659E6F459 /* accountsV2 */; };
|
||||||
92D9088B901CEBB1A99ECA4E /* RoomMemberProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
0BB05221D7D941CC82DC8480 /* LogViewerScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewerScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
@@ -3697,6 +3699,7 @@
|
|||||||
9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */,
|
9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */,
|
||||||
7F957320D0EB7D7B4E30C79D /* KnockRequestProxyMock.swift */,
|
7F957320D0EB7D7B4E30C79D /* KnockRequestProxyMock.swift */,
|
||||||
BA257D747DD7E6FFA5C2BE2D /* LinkNewDeviceServiceMock.swift */,
|
BA257D747DD7E6FFA5C2BE2D /* LinkNewDeviceServiceMock.swift */,
|
||||||
|
0B5DF0E888F66652F8C4CEC5 /* LiveLocationManagerMock.swift */,
|
||||||
6F65E4BB9E82EB8373207CF8 /* MediaProviderMock.swift */,
|
6F65E4BB9E82EB8373207CF8 /* MediaProviderMock.swift */,
|
||||||
8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */,
|
8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */,
|
||||||
840182D7A61402D5947DE094 /* NotificationItemProxyMock.swift */,
|
840182D7A61402D5947DE094 /* NotificationItemProxyMock.swift */,
|
||||||
@@ -8308,6 +8311,7 @@
|
|||||||
74DF5BC17DE9F51E077FD457 /* LinkNewDeviceServiceMock.swift in Sources */,
|
74DF5BC17DE9F51E077FD457 /* LinkNewDeviceServiceMock.swift in Sources */,
|
||||||
866FA35E7A2339EF8B6D91CA /* LinkPreviewView.swift in Sources */,
|
866FA35E7A2339EF8B6D91CA /* LinkPreviewView.swift in Sources */,
|
||||||
03364DE2568860586A713472 /* LiveLocationManager.swift in Sources */,
|
03364DE2568860586A713472 /* LiveLocationManager.swift in Sources */,
|
||||||
|
92919B6522B26B2681232EAC /* LiveLocationManagerMock.swift in Sources */,
|
||||||
CD077E14FAADC444C5A80068 /* LiveLocationManagerProtocol.swift in Sources */,
|
CD077E14FAADC444C5A80068 /* LiveLocationManagerProtocol.swift in Sources */,
|
||||||
C8D0AC22E03F652118A2BB73 /* LiveLocationRoomTimelineItem.swift in Sources */,
|
C8D0AC22E03F652118A2BB73 /* LiveLocationRoomTimelineItem.swift in Sources */,
|
||||||
F7977C53B2B1D73030C69761 /* LiveLocationRoomTimelineView.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 Combine
|
||||||
|
import CoreLocation
|
||||||
@testable import ElementX
|
@testable import ElementX
|
||||||
import Testing
|
import Testing
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
struct LocationSharingScreenViewModelTests {
|
struct LocationSharingScreenViewModelTests {
|
||||||
private let timelineProxy = TimelineProxyMock(.init())
|
var timelineProxy: TimelineProxyMock!
|
||||||
private var viewModel: LocationSharingScreenViewModelProtocol
|
var viewModel: LocationSharingScreenViewModel!
|
||||||
|
|
||||||
private var context: LocationSharingScreenViewModel.Context {
|
var context: LocationSharingScreenViewModel.Context {
|
||||||
viewModel.context
|
viewModel.context
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
let viewModel = LocationSharingScreenViewModel(interactionMode: .picker,
|
AppSettings.resetAllSettings()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func userDidPan() {
|
mutating func userDidPan() {
|
||||||
|
setupViewModel()
|
||||||
#expect(context.viewState.isSharingUserLocation)
|
#expect(context.viewState.isSharingUserLocation)
|
||||||
#expect(context.showsUserLocationMode == .showAndFollow)
|
#expect(context.showsUserLocationMode == .showAndFollow)
|
||||||
context.send(viewAction: .userDidPan)
|
context.send(viewAction: .userDidPan)
|
||||||
@@ -43,7 +35,8 @@ struct LocationSharingScreenViewModelTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func centerOnUser() {
|
mutating func centerOnUser() {
|
||||||
|
setupViewModel()
|
||||||
#expect(context.viewState.isSharingUserLocation)
|
#expect(context.viewState.isSharingUserLocation)
|
||||||
context.showsUserLocationMode = .show
|
context.showsUserLocationMode = .show
|
||||||
#expect(!context.viewState.isSharingUserLocation)
|
#expect(!context.viewState.isSharingUserLocation)
|
||||||
@@ -53,7 +46,8 @@ struct LocationSharingScreenViewModelTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func centerOnUserWithoutAuth() {
|
mutating func centerOnUserWithoutAuth() {
|
||||||
|
setupViewModel()
|
||||||
context.showsUserLocationMode = .hide
|
context.showsUserLocationMode = .hide
|
||||||
context.isLocationAuthorized = nil
|
context.isLocationAuthorized = nil
|
||||||
context.send(viewAction: .centerToUser)
|
context.send(viewAction: .centerToUser)
|
||||||
@@ -61,7 +55,8 @@ struct LocationSharingScreenViewModelTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func centerOnUserWithDeniedAuth() {
|
mutating func centerOnUserWithDeniedAuth() {
|
||||||
|
setupViewModel()
|
||||||
context.isLocationAuthorized = false
|
context.isLocationAuthorized = false
|
||||||
context.showsUserLocationMode = .hide
|
context.showsUserLocationMode = .hide
|
||||||
context.send(viewAction: .centerToUser)
|
context.send(viewAction: .centerToUser)
|
||||||
@@ -70,7 +65,8 @@ struct LocationSharingScreenViewModelTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func errorMapping() {
|
mutating func errorMapping() {
|
||||||
|
setupViewModel()
|
||||||
let mapError = AlertInfo(locationSharingViewError: .mapError(.failedLoadingMap))
|
let mapError = AlertInfo(locationSharingViewError: .mapError(.failedLoadingMap))
|
||||||
#expect(mapError.title == L10n.errorFailedLoadingMap(InfoPlistReader.main.bundleDisplayName))
|
#expect(mapError.title == L10n.errorFailedLoadingMap(InfoPlistReader.main.bundleDisplayName))
|
||||||
let locationError = AlertInfo(locationSharingViewError: .mapError(.failedLocatingUser))
|
let locationError = AlertInfo(locationSharingViewError: .mapError(.failedLocatingUser))
|
||||||
@@ -80,7 +76,8 @@ struct LocationSharingScreenViewModelTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func sendUserLocation() async throws {
|
mutating func sendUserLocation() async throws {
|
||||||
|
setupViewModel()
|
||||||
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
||||||
context.geolocationUncertainty = 10
|
context.geolocationUncertainty = 10
|
||||||
|
|
||||||
@@ -101,7 +98,8 @@ struct LocationSharingScreenViewModelTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func sendPickedLocation() async throws {
|
mutating func sendPickedLocation() async throws {
|
||||||
|
setupViewModel()
|
||||||
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
||||||
context.isLocationAuthorized = nil
|
context.isLocationAuthorized = nil
|
||||||
context.geolocationUncertainty = 10
|
context.geolocationUncertainty = 10
|
||||||
@@ -121,4 +119,101 @@ struct LocationSharingScreenViewModelTests {
|
|||||||
try await deferred.fulfill()
|
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