mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 20:09:04 +00:00
add new tests to LocationSharingViewModel
This commit is contained in:
@@ -21,5 +21,6 @@ extension LiveLocationManagerMock {
|
|||||||
underlyingAuthorizationStatus = .init(authorizationStatusSubject)
|
underlyingAuthorizationStatus = .init(authorizationStatusSubject)
|
||||||
|
|
||||||
requestAlwaysAuthorizationIfPossibleReturnValue = configuration.requestAlwaysAuthorizationIfPossibleReturnValue
|
requestAlwaysAuthorizationIfPossibleReturnValue = configuration.requestAlwaysAuthorizationIfPossibleReturnValue
|
||||||
|
startLiveLocationRoomIDDurationReturnValue = .success(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
|
|||||||
case .close:
|
case .close:
|
||||||
actionsSubject.send(.close)
|
actionsSubject.send(.close)
|
||||||
case .startLiveLocation:
|
case .startLiveLocation:
|
||||||
startLiveLocationSharing()
|
checkAlwaysShareLocationPermission()
|
||||||
case .selectLocation:
|
case .selectLocation:
|
||||||
guard let coordinate = state.bindings.mapCenterLocation else { return }
|
guard let coordinate = state.bindings.mapCenterLocation else { return }
|
||||||
let uncertainty = state.isSharingUserLocation ? context.geolocationUncertainty : nil
|
let uncertainty = state.isSharingUserLocation ? context.geolocationUncertainty : nil
|
||||||
@@ -118,12 +118,8 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
|
|||||||
formatter.allowedUnits = [.hour, .minute]
|
formatter.allowedUnits = [.hour, .minute]
|
||||||
return formatter
|
return formatter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private func startLiveLocationSharing() {
|
private func checkAlwaysShareLocationPermission() {
|
||||||
requestAlwaysLocationPermission()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func requestAlwaysLocationPermission() {
|
|
||||||
authorizationStatusSubscription = nil
|
authorizationStatusSubscription = nil
|
||||||
let authorizationStatus = liveLocationManager.authorizationStatus.value
|
let authorizationStatus = liveLocationManager.authorizationStatus.value
|
||||||
switch authorizationStatus {
|
switch authorizationStatus {
|
||||||
@@ -137,7 +133,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
|
|||||||
.first() // this publisher only fires when there is an actual change, and if the user is done with permissions
|
.first() // this publisher only fires when there is an actual change, and if the user is done with permissions
|
||||||
.sink { [weak self] newValue in
|
.sink { [weak self] newValue in
|
||||||
guard newValue == .authorizedWhenInUse else { return }
|
guard newValue == .authorizedWhenInUse else { return }
|
||||||
self?.requestAlwaysLocationPermission()
|
self?.checkAlwaysShareLocationPermission()
|
||||||
}
|
}
|
||||||
case .authorizedWhenInUse:
|
case .authorizedWhenInUse:
|
||||||
guard liveLocationManager.requestAlwaysAuthorizationIfPossible() else {
|
guard liveLocationManager.requestAlwaysAuthorizationIfPossible() else {
|
||||||
@@ -171,14 +167,14 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func showLiveLocationDurationPicker() {
|
private func showLiveLocationDurationPicker() {
|
||||||
let durations: [TimeInterval] = [15 * 60, // 15 minutes
|
let durations: [Duration] = [.seconds(15 * 60), // 15 minutes
|
||||||
60 * 60, // 1 hour
|
.seconds(60 * 60), // 1 hour
|
||||||
8 * 60 * 60] // 8 hours
|
.seconds(60 * 60 * 8)] // 8 hours
|
||||||
|
|
||||||
let durationButtons: [AlertInfo<LocationSharingViewAlert>.AlertButton] = durations.compactMap { duration in
|
let durationButtons: [AlertInfo<LocationSharingViewAlert>.AlertButton] = durations.compactMap { duration in
|
||||||
guard let title = Self.durationFormatter.string(from: duration) else { return nil }
|
guard let title = Self.durationFormatter.string(from: duration.seconds) else { return nil }
|
||||||
return .init(title: title) { [weak self] in
|
return .init(title: title) { [weak self] in
|
||||||
Task { [weak self] in await self?.startLiveLocationSharingInRoom(durationMillis: UInt64(duration * 1000)) }
|
Task { [weak self] in await self?.startLiveLocationSharingInRoom(duration: duration) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,9 +183,9 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
|
|||||||
verticalButtons: durationButtons)
|
verticalButtons: durationButtons)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startLiveLocationSharingInRoom(durationMillis: UInt64) async {
|
private func startLiveLocationSharingInRoom(duration: Duration) async {
|
||||||
let result = await liveLocationManager.startLiveLocation(roomID: roomProxy.id,
|
let result = await liveLocationManager.startLiveLocation(roomID: roomProxy.id,
|
||||||
durationMillis: durationMillis)
|
duration: duration)
|
||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
|
|||||||
@@ -71,11 +71,11 @@ final class LocationSharingScreenViewModelTests {
|
|||||||
@Test
|
@Test
|
||||||
func errorMapping() {
|
func errorMapping() {
|
||||||
setupViewModel()
|
setupViewModel()
|
||||||
let mapError = AlertInfo(locationSharingViewError: .mapError(.failedLoadingMap))
|
let mapError = AlertInfo(alertID: .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(alertID: .mapError(.failedLocatingUser))
|
||||||
#expect(locationError.title == L10n.errorFailedLocatingUser(InfoPlistReader.main.bundleDisplayName))
|
#expect(locationError.title == L10n.errorFailedLocatingUser(InfoPlistReader.main.bundleDisplayName))
|
||||||
let AuthorizationError = AlertInfo(locationSharingViewError: .missingAuthorization)
|
let AuthorizationError = AlertInfo(alertID: .missingAuthorization)
|
||||||
#expect(AuthorizationError.message == L10n.dialogPermissionLocationDescriptionIos(InfoPlistReader.main.bundleDisplayName))
|
#expect(AuthorizationError.message == L10n.dialogPermissionLocationDescriptionIos(InfoPlistReader.main.bundleDisplayName))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,13 +140,6 @@ final class LocationSharingScreenViewModelTests {
|
|||||||
#expect(context.alertInfo?.id == .missingAlwaysAuthorization)
|
#expect(context.alertInfo?.id == .missingAlwaysAuthorization)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
func startLiveLocationWithAlwaysAuthorization() {
|
|
||||||
setupViewModel(liveLocationManagerConfiguration: .init(authorizationStatus: .authorizedAlways))
|
|
||||||
context.send(viewAction: .startLiveLocation)
|
|
||||||
#expect(context.alertInfo == nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func startLiveLocationWithWhenInUseAuthorizationAlreadyRequested() {
|
func startLiveLocationWithWhenInUseAuthorizationAlreadyRequested() {
|
||||||
setupViewModel(liveLocationManagerConfiguration: .init(authorizationStatus: .authorizedWhenInUse,
|
setupViewModel(liveLocationManagerConfiguration: .init(authorizationStatus: .authorizedWhenInUse,
|
||||||
@@ -191,8 +184,81 @@ final class LocationSharingScreenViewModelTests {
|
|||||||
#expect(context.alertInfo == nil)
|
#expect(context.alertInfo == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Live Location Start Flow Tests
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func startLiveLocationShowsDisclaimer() {
|
||||||
|
setupViewModel(liveLocationManagerConfiguration: .init(authorizationStatus: .authorizedAlways))
|
||||||
|
context.send(viewAction: .startLiveLocation)
|
||||||
|
#expect(context.alertInfo?.id == .liveLocationDisclaimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func startLiveLocationDisclaimerDeclineSkipsStart() {
|
||||||
|
let liveLocationManagerMock = LiveLocationManagerMock(.init(authorizationStatus: .authorizedAlways))
|
||||||
|
setupViewModel(liveLocationManagerMock: liveLocationManagerMock)
|
||||||
|
context.send(viewAction: .startLiveLocation)
|
||||||
|
context.alertInfo?.primaryButton.action?()
|
||||||
|
#expect(!liveLocationManagerMock.startLiveLocationRoomIDDurationCalled)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func startLiveLocationDisclaimerAcceptShowsDurationPicker() async throws {
|
||||||
|
setupViewModel(liveLocationManagerConfiguration: .init(authorizationStatus: .authorizedAlways))
|
||||||
|
context.send(viewAction: .startLiveLocation)
|
||||||
|
#expect(context.alertInfo?.id == .liveLocationDisclaimer)
|
||||||
|
let deferred = deferFulfillment(context.observe(\.alertInfo)) { $0?.id == .liveLocationDurationSelection }
|
||||||
|
context.alertInfo?.secondaryButton?.action?()
|
||||||
|
try await deferred.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func startLiveLocationDurationPickerCancelSkipsStart() async throws {
|
||||||
|
let liveLocationManagerMock = LiveLocationManagerMock(.init(authorizationStatus: .authorizedAlways))
|
||||||
|
setupViewModel(liveLocationManagerMock: liveLocationManagerMock)
|
||||||
|
context.send(viewAction: .startLiveLocation)
|
||||||
|
let deferred = deferFulfillment(context.observe(\.alertInfo)) { $0?.id == .liveLocationDurationSelection }
|
||||||
|
context.alertInfo?.secondaryButton?.action?()
|
||||||
|
try await deferred.fulfill()
|
||||||
|
context.alertInfo?.primaryButton.action?()
|
||||||
|
#expect(!liveLocationManagerMock.startLiveLocationRoomIDDurationCalled)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func startLiveLocationSuccess() async throws {
|
||||||
|
let liveLocationManagerMock = LiveLocationManagerMock(.init(authorizationStatus: .authorizedAlways))
|
||||||
|
setupViewModel(liveLocationManagerMock: liveLocationManagerMock)
|
||||||
|
context.send(viewAction: .startLiveLocation)
|
||||||
|
let durationPicker = deferFulfillment(context.observe(\.alertInfo)) { $0?.id == .liveLocationDurationSelection }
|
||||||
|
context.alertInfo?.secondaryButton?.action?()
|
||||||
|
try await durationPicker.fulfill()
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
||||||
|
context.alertInfo?.verticalButtons?.first?.action?()
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
#expect(liveLocationManagerMock.startLiveLocationRoomIDDurationCalled)
|
||||||
|
let arguments = try #require(liveLocationManagerMock.startLiveLocationRoomIDDurationReceivedArguments)
|
||||||
|
#expect(arguments.duration == .seconds(60 * 15))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func startLiveLocationFailureDoesNotClose() async throws {
|
||||||
|
let liveLocationManagerMock = LiveLocationManagerMock(.init(authorizationStatus: .authorizedAlways))
|
||||||
|
liveLocationManagerMock.startLiveLocationRoomIDDurationReturnValue = .failure(.startFailed)
|
||||||
|
setupViewModel(liveLocationManagerMock: liveLocationManagerMock)
|
||||||
|
context.send(viewAction: .startLiveLocation)
|
||||||
|
let durationPicker = deferFulfillment(context.observe(\.alertInfo)) { $0?.id == .liveLocationDurationSelection }
|
||||||
|
context.alertInfo?.secondaryButton?.action?()
|
||||||
|
try await durationPicker.fulfill()
|
||||||
|
|
||||||
|
let deferredFailure = deferFailure(viewModel.actions, timeout: .seconds(1)) { $0 == .close }
|
||||||
|
context.alertInfo?.verticalButtons?.first?.action?()
|
||||||
|
try await deferredFailure.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func setupViewModel(liveLocationManagerConfiguration: LiveLocationManagerMock.Configuration = .init()) {
|
private func setupViewModel(liveLocationManagerConfiguration: LiveLocationManagerMock.Configuration = .init()) {
|
||||||
timelineProxy = TimelineProxyMock(.init())
|
timelineProxy = TimelineProxyMock(.init())
|
||||||
viewModel = LocationSharingScreenViewModel(interactionMode: .picker,
|
viewModel = LocationSharingScreenViewModel(interactionMode: .picker,
|
||||||
|
|||||||
Reference in New Issue
Block a user