Fix and add tests related to location
This commit is contained in:
parent
392fa9de9b
commit
8274ef220d
5 changed files with 404 additions and 291 deletions
|
|
@ -57,12 +57,14 @@ class ShareLocationStateProvider : PreviewParameterProvider<ShareLocationState>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun aShareLocationState(
|
fun aShareLocationState(
|
||||||
currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")),
|
currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")),
|
||||||
dialogState: ShareLocationState.Dialog,
|
dialogState: ShareLocationState.Dialog = ShareLocationState.Dialog.None,
|
||||||
trackUserPosition: Boolean,
|
trackUserPosition: Boolean = false,
|
||||||
hasLocationPermission: Boolean,
|
hasLocationPermission: Boolean = false,
|
||||||
canShareLiveLocation: Boolean = false,
|
canShareLiveLocation: Boolean = false,
|
||||||
|
appName: String = APP_NAME,
|
||||||
|
eventSink: (ShareLocationEvent) -> Unit = {},
|
||||||
): ShareLocationState {
|
): ShareLocationState {
|
||||||
return ShareLocationState(
|
return ShareLocationState(
|
||||||
currentUser = currentUser,
|
currentUser = currentUser,
|
||||||
|
|
@ -70,7 +72,7 @@ private fun aShareLocationState(
|
||||||
trackUserLocation = trackUserPosition,
|
trackUserLocation = trackUserPosition,
|
||||||
hasLocationPermission = hasLocationPermission,
|
hasLocationPermission = hasLocationPermission,
|
||||||
canShareLiveLocation = canShareLiveLocation,
|
canShareLiveLocation = canShareLiveLocation,
|
||||||
appName = APP_NAME,
|
appName = appName,
|
||||||
eventSink = {}
|
eventSink = eventSink
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,22 +20,25 @@ import io.element.android.features.location.impl.common.permissions.FakePermissi
|
||||||
import io.element.android.features.location.impl.common.permissions.PermissionsEvents
|
import io.element.android.features.location.impl.common.permissions.PermissionsEvents
|
||||||
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
|
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
|
||||||
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
||||||
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.features.messages.test.FakeMessageComposerContext
|
import io.element.android.features.messages.test.FakeMessageComposerContext
|
||||||
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId
|
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||||
import io.element.android.libraries.textcomposer.model.MessageComposerMode
|
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import io.element.android.tests.testutils.lambda.value
|
import io.element.android.tests.testutils.lambda.value
|
||||||
|
import io.element.android.tests.testutils.test
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
@ -49,9 +52,12 @@ class ShareLocationPresenterTest {
|
||||||
private val fakeMessageComposerContext = FakeMessageComposerContext()
|
private val fakeMessageComposerContext = FakeMessageComposerContext()
|
||||||
private val fakeLocationActions = FakeLocationActions()
|
private val fakeLocationActions = FakeLocationActions()
|
||||||
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
|
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
|
||||||
|
private val fakeFeatureFlagService = FakeFeatureFlagService()
|
||||||
|
private val fakeMatrixClient = FakeMatrixClient(sessionId = A_USER_ID)
|
||||||
|
|
||||||
private fun createShareLocationPresenter(
|
private fun createShareLocationPresenter(
|
||||||
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
||||||
|
locationActions: FakeLocationActions = fakeLocationActions,
|
||||||
): ShareLocationPresenter = ShareLocationPresenter(
|
): ShareLocationPresenter = ShareLocationPresenter(
|
||||||
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
|
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
|
||||||
override fun create(permissions: List<String>): PermissionsPresenter = fakePermissionsPresenter
|
override fun create(permissions: List<String>): PermissionsPresenter = fakePermissionsPresenter
|
||||||
|
|
@ -60,13 +66,14 @@ class ShareLocationPresenterTest {
|
||||||
timelineMode = Timeline.Mode.Live,
|
timelineMode = Timeline.Mode.Live,
|
||||||
analyticsService = fakeAnalyticsService,
|
analyticsService = fakeAnalyticsService,
|
||||||
messageComposerContext = fakeMessageComposerContext,
|
messageComposerContext = fakeMessageComposerContext,
|
||||||
locationActions = fakeLocationActions,
|
locationActions = locationActions,
|
||||||
buildMeta = fakeBuildMeta,
|
buildMeta = fakeBuildMeta,
|
||||||
|
featureFlagService = fakeFeatureFlagService,
|
||||||
|
client = fakeMatrixClient,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions granted`() = runTest {
|
fun `initial state with permissions granted and location enabled`() = runTest {
|
||||||
val shareLocationPresenter = createShareLocationPresenter()
|
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.AllGranted,
|
permissions = PermissionsState.Permissions.AllGranted,
|
||||||
|
|
@ -74,25 +81,18 @@ class ShareLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
shareLocationPresenter.present()
|
shareLocationPresenter.test {
|
||||||
}.test {
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val state = awaitItem()
|
||||||
assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
assertThat(state.trackUserLocation).isTrue()
|
||||||
assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.SenderLocation)
|
assertThat(state.hasLocationPermission).isTrue()
|
||||||
assertThat(initialState.hasLocationPermission).isTrue()
|
assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
||||||
|
|
||||||
// Swipe the map to switch mode
|
|
||||||
initialState.eventSink(ShareLocationEvent.SwitchToPinLocationMode)
|
|
||||||
val myLocationState = awaitItem()
|
|
||||||
assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
|
||||||
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(myLocationState.hasLocationPermission).isTrue()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions partially granted`() = runTest {
|
fun `initial state with permissions partially granted and location enabled`() = runTest {
|
||||||
val shareLocationPresenter = createShareLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
|
|
@ -104,17 +104,11 @@ class ShareLocationPresenterTest {
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
shareLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
assertThat(initialState.trackUserLocation).isTrue()
|
||||||
assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.SenderLocation)
|
|
||||||
assertThat(initialState.hasLocationPermission).isTrue()
|
assertThat(initialState.hasLocationPermission).isTrue()
|
||||||
|
assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
||||||
// Swipe the map to switch mode
|
|
||||||
initialState.eventSink(ShareLocationEvent.SwitchToPinLocationMode)
|
|
||||||
val myLocationState = awaitItem()
|
|
||||||
assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
|
||||||
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(myLocationState.hasLocationPermission).isTrue()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,22 +125,18 @@ class ShareLocationPresenterTest {
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
shareLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
assertThat(initialState.trackUserLocation).isFalse()
|
||||||
assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(initialState.hasLocationPermission).isFalse()
|
assertThat(initialState.hasLocationPermission).isFalse()
|
||||||
|
assertThat(initialState.dialogState).isEqualTo(
|
||||||
// Click on the button to switch mode
|
ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionDenied)
|
||||||
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
|
)
|
||||||
val myLocationState = awaitItem()
|
|
||||||
assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.PermissionDenied)
|
|
||||||
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(myLocationState.hasLocationPermission).isFalse()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions denied once`() = runTest {
|
fun `initial state with permissions denied with rationale`() = runTest {
|
||||||
val shareLocationPresenter = createShareLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
|
|
@ -155,25 +145,62 @@ class ShareLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
shareLocationPresenter.test {
|
||||||
shareLocationPresenter.present()
|
skipItems(1)
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
assertThat(initialState.trackUserLocation).isFalse()
|
||||||
assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(initialState.hasLocationPermission).isFalse()
|
assertThat(initialState.hasLocationPermission).isFalse()
|
||||||
|
assertThat(initialState.dialogState).isEqualTo(
|
||||||
// Click on the button to switch mode
|
ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale)
|
||||||
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
|
)
|
||||||
val myLocationState = awaitItem()
|
|
||||||
assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
|
|
||||||
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(myLocationState.hasLocationPermission).isFalse()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `rationale dialog dismiss`() = runTest {
|
fun `initial state with location services disabled`() = runTest {
|
||||||
|
val locationActions = FakeLocationActions(isLocationEnabled = false)
|
||||||
|
val shareLocationPresenter = createShareLocationPresenter(locationActions = locationActions)
|
||||||
|
fakePermissionsPresenter.givenState(
|
||||||
|
aPermissionsState(
|
||||||
|
permissions = PermissionsState.Permissions.AllGranted,
|
||||||
|
shouldShowRationale = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
shareLocationPresenter.test {
|
||||||
|
skipItems(1)
|
||||||
|
val initialState = awaitItem()
|
||||||
|
assertThat(initialState.trackUserLocation).isFalse()
|
||||||
|
assertThat(initialState.hasLocationPermission).isTrue()
|
||||||
|
assertThat(initialState.dialogState).isEqualTo(
|
||||||
|
ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `StopTrackingUserLocation event sets trackUserLocation to false`() = runTest {
|
||||||
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
|
fakePermissionsPresenter.givenState(
|
||||||
|
aPermissionsState(
|
||||||
|
permissions = PermissionsState.Permissions.AllGranted,
|
||||||
|
shouldShowRationale = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
shareLocationPresenter.test {
|
||||||
|
skipItems(1)
|
||||||
|
val initialState = awaitItem()
|
||||||
|
assertThat(initialState.trackUserLocation).isTrue()
|
||||||
|
|
||||||
|
initialState.eventSink(ShareLocationEvent.StopTrackingUserLocation)
|
||||||
|
val stoppedState = awaitItem()
|
||||||
|
assertThat(stoppedState.trackUserLocation).isFalse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `DismissDialog event clears dialog state`() = runTest {
|
||||||
val shareLocationPresenter = createShareLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
|
|
@ -182,30 +209,21 @@ class ShareLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
shareLocationPresenter.test {
|
||||||
shareLocationPresenter.present()
|
skipItems(1)
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
assertThat(initialState.dialogState).isEqualTo(
|
||||||
|
ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale)
|
||||||
|
)
|
||||||
|
|
||||||
// Click on the button to switch mode
|
initialState.eventSink(ShareLocationEvent.DismissDialog)
|
||||||
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
|
val dismissedState = awaitItem()
|
||||||
val myLocationState = awaitItem()
|
assertThat(dismissedState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
|
|
||||||
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(myLocationState.hasLocationPermission).isFalse()
|
|
||||||
|
|
||||||
// Dismiss the dialog
|
|
||||||
myLocationState.eventSink(ShareLocationEvent.DismissDialog)
|
|
||||||
val dialogDismissedState = awaitItem()
|
|
||||||
assertThat(dialogDismissedState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
|
||||||
assertThat(dialogDismissedState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(dialogDismissedState.hasLocationPermission).isFalse()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `rationale dialog continue`() = runTest {
|
fun `RequestPermissions event triggers permission request`() = runTest {
|
||||||
val shareLocationPresenter = createShareLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
|
|
@ -214,27 +232,20 @@ class ShareLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
shareLocationPresenter.test {
|
||||||
shareLocationPresenter.present()
|
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
initialState.eventSink(ShareLocationEvent.RequestPermissions)
|
||||||
|
|
||||||
// Click on the button to switch mode
|
// Wait for dialog to be dismissed
|
||||||
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
|
awaitItem()
|
||||||
val myLocationState = awaitItem()
|
|
||||||
assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
|
|
||||||
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(myLocationState.hasLocationPermission).isFalse()
|
|
||||||
|
|
||||||
// Continue the dialog sends permission request to the permissions presenter
|
|
||||||
myLocationState.eventSink(ShareLocationEvent.StartTrackingUserLocation)
|
|
||||||
assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvents.RequestPermissions)
|
assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvents.RequestPermissions)
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `permission denied dialog dismiss`() = runTest {
|
fun `OpenAppSettings event opens settings and clears dialog`() = runTest {
|
||||||
val shareLocationPresenter = createShareLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
|
|
@ -243,31 +254,94 @@ class ShareLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
shareLocationPresenter.test {
|
||||||
shareLocationPresenter.present()
|
skipItems(1)
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
initialState.eventSink(ShareLocationEvent.OpenAppSettings)
|
||||||
|
val settingsOpenedState = awaitItem()
|
||||||
|
|
||||||
// Click on the button to switch mode
|
assertThat(settingsOpenedState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
|
assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1)
|
||||||
val myLocationState = awaitItem()
|
|
||||||
assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.PermissionDenied)
|
|
||||||
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(myLocationState.hasLocationPermission).isFalse()
|
|
||||||
|
|
||||||
// Dismiss the dialog
|
|
||||||
myLocationState.eventSink(ShareLocationEvent.DismissDialog)
|
|
||||||
val dialogDismissedState = awaitItem()
|
|
||||||
assertThat(dialogDismissedState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
|
||||||
assertThat(dialogDismissedState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
|
||||||
assertThat(dialogDismissedState.hasLocationPermission).isFalse()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `share sender location`() = runTest {
|
fun `OpenLocationSettings event opens location settings and clears dialog`() = runTest {
|
||||||
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, EventId?, Result<Unit>> { _, _, _, _, _, _ ->
|
val locationActions = FakeLocationActions(isLocationEnabled = false)
|
||||||
|
val shareLocationPresenter = createShareLocationPresenter(locationActions = locationActions)
|
||||||
|
fakePermissionsPresenter.givenState(
|
||||||
|
aPermissionsState(
|
||||||
|
permissions = PermissionsState.Permissions.AllGranted,
|
||||||
|
shouldShowRationale = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
shareLocationPresenter.test {
|
||||||
|
skipItems(1)
|
||||||
|
val initialState = awaitItem()
|
||||||
|
assertThat(initialState.dialogState).isEqualTo(
|
||||||
|
ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled)
|
||||||
|
)
|
||||||
|
|
||||||
|
initialState.eventSink(ShareLocationEvent.OpenLocationSettings)
|
||||||
|
val settingsOpenedState = awaitItem()
|
||||||
|
|
||||||
|
assertThat(settingsOpenedState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
|
assertThat(locationActions.openLocationSettingsInvocationsCount).isEqualTo(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ShowLiveLocationDurationPicker shows duration dialog when constraints pass`() = runTest {
|
||||||
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
|
fakePermissionsPresenter.givenState(
|
||||||
|
aPermissionsState(
|
||||||
|
permissions = PermissionsState.Permissions.AllGranted,
|
||||||
|
shouldShowRationale = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
shareLocationPresenter.test {
|
||||||
|
skipItems(1)
|
||||||
|
val initialState = awaitItem()
|
||||||
|
initialState.eventSink(ShareLocationEvent.ShowLiveLocationDurationPicker)
|
||||||
|
val durationDialogState = awaitItem()
|
||||||
|
|
||||||
|
assertThat(durationDialogState.dialogState).isEqualTo(ShareLocationState.Dialog.LiveLocationDuration)
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ShowLiveLocationDurationPicker shows constraint dialog when permissions denied`() = runTest {
|
||||||
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
|
fakePermissionsPresenter.givenState(
|
||||||
|
aPermissionsState(
|
||||||
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
shouldShowRationale = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
shareLocationPresenter.test {
|
||||||
|
skipItems(1)
|
||||||
|
val initialState = awaitItem()
|
||||||
|
// Dismiss initial dialog
|
||||||
|
initialState.eventSink(ShareLocationEvent.DismissDialog)
|
||||||
|
val dismissedState = awaitItem()
|
||||||
|
|
||||||
|
dismissedState.eventSink(ShareLocationEvent.ShowLiveLocationDurationPicker)
|
||||||
|
val constraintDialogState = awaitItem()
|
||||||
|
|
||||||
|
assertThat(constraintDialogState.dialogState).isEqualTo(
|
||||||
|
ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionDenied)
|
||||||
|
)
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ShareStaticLocation sends user location`() = runTest {
|
||||||
|
val sendLocationResult = lambdaRecorder { _: String, _: String, _: String?, _: Int?, _: AssetType?, _: EventId? ->
|
||||||
Result.success(Unit)
|
Result.success(Unit)
|
||||||
}
|
}
|
||||||
val joinedRoom = FakeJoinedRoom(
|
val joinedRoom = FakeJoinedRoom(
|
||||||
|
|
@ -283,29 +357,18 @@ class ShareLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
shareLocationPresenter.test {
|
||||||
shareLocationPresenter.present()
|
skipItems(1)
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
// Send location
|
|
||||||
initialState.eventSink(
|
initialState.eventSink(
|
||||||
ShareLocationEvent.ShareStaticLocation(
|
ShareLocationEvent.ShareStaticLocation(
|
||||||
cameraPosition = ShareLocationEvent.ShareStaticLocation.CameraPosition(
|
location = Location(lat = 3.0, lon = 4.0, accuracy = 5.0f),
|
||||||
lat = 0.0,
|
isPinned = false,
|
||||||
lon = 1.0,
|
|
||||||
zoom = 2.0,
|
|
||||||
),
|
|
||||||
location = Location(
|
|
||||||
lat = 3.0,
|
|
||||||
lon = 4.0,
|
|
||||||
accuracy = 5.0f,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
delay(1) // Wait for the coroutine to finish
|
advanceUntilIdle()
|
||||||
|
|
||||||
sendLocationResult.assertions().isCalledOnce()
|
sendLocationResult.assertions().isCalledOnce()
|
||||||
.with(
|
.with(
|
||||||
|
|
@ -326,12 +389,13 @@ class ShareLocationPresenterTest {
|
||||||
messageType = Composer.MessageType.LocationUser,
|
messageType = Composer.MessageType.LocationUser,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `share pin location`() = runTest {
|
fun `ShareStaticLocation sends pinned location`() = runTest {
|
||||||
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, EventId?, Result<Unit>> { _, _, _, _, _, _ ->
|
val sendLocationResult = lambdaRecorder { _: String, _: String, _: String?, _: Int?, _: AssetType?, _: EventId? ->
|
||||||
Result.success(Unit)
|
Result.success(Unit)
|
||||||
}
|
}
|
||||||
val joinedRoom = FakeJoinedRoom(
|
val joinedRoom = FakeJoinedRoom(
|
||||||
|
|
@ -342,39 +406,26 @@ class ShareLocationPresenterTest {
|
||||||
val shareLocationPresenter = createShareLocationPresenter(joinedRoom)
|
val shareLocationPresenter = createShareLocationPresenter(joinedRoom)
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.AllGranted,
|
||||||
shouldShowRationale = false,
|
shouldShowRationale = false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
shareLocationPresenter.test {
|
||||||
shareLocationPresenter.present()
|
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
// Send location
|
|
||||||
initialState.eventSink(
|
initialState.eventSink(
|
||||||
ShareLocationEvent.ShareStaticLocation(
|
ShareLocationEvent.ShareStaticLocation(
|
||||||
cameraPosition = ShareLocationEvent.ShareStaticLocation.CameraPosition(
|
location = Location(lat = 1.0, lon = 2.0, accuracy = 3.0f),
|
||||||
lat = 0.0,
|
isPinned = true,
|
||||||
lon = 1.0,
|
|
||||||
zoom = 2.0,
|
|
||||||
),
|
|
||||||
location = Location(
|
|
||||||
lat = 3.0,
|
|
||||||
lon = 4.0,
|
|
||||||
accuracy = 5.0f,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
delay(1) // Wait for the coroutine to finish
|
advanceUntilIdle()
|
||||||
|
|
||||||
sendLocationResult.assertions().isCalledOnce()
|
sendLocationResult.assertions().isCalledOnce()
|
||||||
.with(
|
.with(
|
||||||
value("Location was shared at geo:0.0,1.0"),
|
value("Location was shared at geo:1.0,2.0;u=3.0"),
|
||||||
value("geo:0.0,1.0"),
|
value("geo:1.0,2.0;u=3.0"),
|
||||||
value(null),
|
value(null),
|
||||||
value(15),
|
value(15),
|
||||||
value(AssetType.PIN),
|
value(AssetType.PIN),
|
||||||
|
|
@ -390,107 +441,7 @@ class ShareLocationPresenterTest {
|
||||||
messageType = Composer.MessageType.LocationPin,
|
messageType = Composer.MessageType.LocationPin,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `composer context passes through analytics`() = runTest {
|
|
||||||
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, EventId?, Result<Unit>> { _, _, _, _, _, _ ->
|
|
||||||
Result.success(Unit)
|
|
||||||
}
|
|
||||||
val joinedRoom = FakeJoinedRoom(
|
|
||||||
liveTimeline = FakeTimeline().apply {
|
|
||||||
sendLocationLambda = sendLocationResult
|
|
||||||
},
|
|
||||||
)
|
|
||||||
val shareLocationPresenter = createShareLocationPresenter(joinedRoom)
|
|
||||||
fakePermissionsPresenter.givenState(
|
|
||||||
aPermissionsState(
|
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
|
||||||
shouldShowRationale = false,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fakeMessageComposerContext.apply {
|
|
||||||
composerMode = MessageComposerMode.Edit(
|
|
||||||
eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
|
||||||
content = ""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
shareLocationPresenter.present()
|
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
|
||||||
val initialState = awaitItem()
|
|
||||||
|
|
||||||
// Send location
|
|
||||||
initialState.eventSink(
|
|
||||||
ShareLocationEvent.ShareStaticLocation(
|
|
||||||
cameraPosition = ShareLocationEvent.ShareStaticLocation.CameraPosition(
|
|
||||||
lat = 0.0,
|
|
||||||
lon = 1.0,
|
|
||||||
zoom = 2.0,
|
|
||||||
),
|
|
||||||
location = null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
delay(1) // Wait for the coroutine to finish
|
|
||||||
|
|
||||||
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
|
|
||||||
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
|
|
||||||
Composer(
|
|
||||||
inThread = false,
|
|
||||||
isEditing = true,
|
|
||||||
isReply = false,
|
|
||||||
messageType = Composer.MessageType.LocationPin,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `open settings activity`() = runTest {
|
|
||||||
val shareLocationPresenter = createShareLocationPresenter()
|
|
||||||
fakePermissionsPresenter.givenState(
|
|
||||||
aPermissionsState(
|
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
|
||||||
shouldShowRationale = false,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fakeMessageComposerContext.apply {
|
|
||||||
composerMode = MessageComposerMode.Edit(
|
|
||||||
eventOrTransactionId = AN_EVENT_ID.toEventOrTransactionId(),
|
|
||||||
content = ""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
shareLocationPresenter.present()
|
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
|
||||||
val initialState = awaitItem()
|
|
||||||
|
|
||||||
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
|
|
||||||
val dialogShownState = awaitItem()
|
|
||||||
|
|
||||||
// Open settings
|
|
||||||
dialogShownState.eventSink(ShareLocationEvent.OpenAppSettings)
|
|
||||||
val settingsOpenedState = awaitItem()
|
|
||||||
|
|
||||||
assertThat(settingsOpenedState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
|
|
||||||
assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `application name is in state`() = runTest {
|
|
||||||
val shareLocationPresenter = createShareLocationPresenter()
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
shareLocationPresenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.appName).isEqualTo("app name")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Element Creations Ltd.
|
||||||
|
* Copyright 2024, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.location.impl.share
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.ui.platform.LocalInspectionMode
|
||||||
|
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||||
|
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||||
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
|
import androidx.compose.ui.test.performClick
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
|
import io.element.android.libraries.testtags.TestTags
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||||
|
import io.element.android.tests.testutils.EventsRecorder
|
||||||
|
import io.element.android.tests.testutils.clickOn
|
||||||
|
import io.element.android.tests.testutils.ensureCalledOnce
|
||||||
|
import io.element.android.tests.testutils.pressBack
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.rules.TestRule
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ShareLocationViewTest {
|
||||||
|
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test back action`() {
|
||||||
|
val eventsRecorder = EventsRecorder<ShareLocationEvent>(expectEvents = false)
|
||||||
|
ensureCalledOnce { callback ->
|
||||||
|
rule.setShareLocationView(
|
||||||
|
state = aShareLocationState(
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
navigateUp = callback,
|
||||||
|
)
|
||||||
|
rule.pressBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test fab click`() {
|
||||||
|
val eventsRecorder = EventsRecorder<ShareLocationEvent>()
|
||||||
|
rule.setShareLocationView(
|
||||||
|
aShareLocationState(
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
navigateUp = EnsureNeverCalled(),
|
||||||
|
)
|
||||||
|
rule.onNodeWithTag(TestTags.floatingActionButton.value).performClick()
|
||||||
|
eventsRecorder.assertSingle(ShareLocationEvent.StartTrackingUserLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when permission denied is displayed user can open the settings`() {
|
||||||
|
val eventsRecorder = EventsRecorder<ShareLocationEvent>()
|
||||||
|
rule.setShareLocationView(
|
||||||
|
aShareLocationState(
|
||||||
|
dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionDenied),
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
navigateUp = EnsureNeverCalled(),
|
||||||
|
)
|
||||||
|
rule.clickOn(CommonStrings.action_continue)
|
||||||
|
eventsRecorder.assertSingle(ShareLocationEvent.OpenAppSettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when permission denied is displayed user can close the dialog`() {
|
||||||
|
val eventsRecorder = EventsRecorder<ShareLocationEvent>()
|
||||||
|
rule.setShareLocationView(
|
||||||
|
aShareLocationState(
|
||||||
|
dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionDenied),
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
navigateUp = EnsureNeverCalled(),
|
||||||
|
)
|
||||||
|
rule.clickOn(CommonStrings.action_cancel)
|
||||||
|
eventsRecorder.assertSingle(ShareLocationEvent.DismissDialog)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when permission rationale is displayed user can request permissions`() {
|
||||||
|
val eventsRecorder = EventsRecorder<ShareLocationEvent>()
|
||||||
|
rule.setShareLocationView(
|
||||||
|
aShareLocationState(
|
||||||
|
dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale),
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
navigateUp = EnsureNeverCalled(),
|
||||||
|
)
|
||||||
|
rule.clickOn(CommonStrings.action_continue)
|
||||||
|
eventsRecorder.assertSingle(ShareLocationEvent.RequestPermissions)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when permission rationale is displayed user can close the dialog`() {
|
||||||
|
val eventsRecorder = EventsRecorder<ShareLocationEvent>()
|
||||||
|
rule.setShareLocationView(
|
||||||
|
aShareLocationState(
|
||||||
|
dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale),
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
navigateUp = EnsureNeverCalled(),
|
||||||
|
)
|
||||||
|
rule.clickOn(CommonStrings.action_cancel)
|
||||||
|
eventsRecorder.assertSingle(ShareLocationEvent.DismissDialog)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when location service disabled is displayed user can open location settings`() {
|
||||||
|
val eventsRecorder = EventsRecorder<ShareLocationEvent>()
|
||||||
|
rule.setShareLocationView(
|
||||||
|
aShareLocationState(
|
||||||
|
dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled),
|
||||||
|
hasLocationPermission = true,
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
navigateUp = EnsureNeverCalled(),
|
||||||
|
)
|
||||||
|
rule.clickOn(CommonStrings.action_continue)
|
||||||
|
eventsRecorder.assertSingle(ShareLocationEvent.OpenLocationSettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when location service disabled is displayed user can close the dialog`() {
|
||||||
|
val eventsRecorder = EventsRecorder<ShareLocationEvent>()
|
||||||
|
rule.setShareLocationView(
|
||||||
|
aShareLocationState(
|
||||||
|
dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled),
|
||||||
|
hasLocationPermission = true,
|
||||||
|
eventSink = eventsRecorder
|
||||||
|
),
|
||||||
|
navigateUp = EnsureNeverCalled(),
|
||||||
|
)
|
||||||
|
rule.clickOn(CommonStrings.action_cancel)
|
||||||
|
eventsRecorder.assertSingle(ShareLocationEvent.DismissDialog)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setShareLocationView(
|
||||||
|
state: ShareLocationState,
|
||||||
|
navigateUp: () -> Unit = EnsureNeverCalled(),
|
||||||
|
) {
|
||||||
|
setContent {
|
||||||
|
// Simulate a LocalInspectionMode for MapLibreMap
|
||||||
|
CompositionLocalProvider(LocalInspectionMode provides true) {
|
||||||
|
ShareLocationView(
|
||||||
|
state = state,
|
||||||
|
navigateUp = navigateUp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,8 +13,11 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.location.api.ShowLocationEntryPoint
|
import io.element.android.features.location.api.ShowLocationEntryPoint
|
||||||
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
import io.element.android.features.location.impl.common.actions.FakeLocationActions
|
import io.element.android.features.location.impl.common.actions.FakeLocationActions
|
||||||
import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter
|
import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter
|
||||||
|
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||||
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.node.TestParentNode
|
import io.element.android.tests.testutils.node.TestParentNode
|
||||||
|
|
@ -32,21 +35,27 @@ class DefaultShowLocationEntryPointTest {
|
||||||
ShowLocationNode(
|
ShowLocationNode(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
presenterFactory = { location: Location, description: String? ->
|
presenterFactory = object : ShowLocationPresenter.Factory {
|
||||||
ShowLocationPresenter(
|
override fun create(mode: ShowLocationMode) = ShowLocationPresenter(
|
||||||
|
mode = mode,
|
||||||
permissionsPresenterFactory = { FakePermissionsPresenter() },
|
permissionsPresenterFactory = { FakePermissionsPresenter() },
|
||||||
locationActions = FakeLocationActions(),
|
locationActions = FakeLocationActions(),
|
||||||
buildMeta = aBuildMeta(),
|
buildMeta = aBuildMeta(),
|
||||||
location = location,
|
dateFormatter = FakeDateFormatter(),
|
||||||
description = description,
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
analyticsService = FakeAnalyticsService(),
|
analyticsService = FakeAnalyticsService(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val inputs = ShowLocationEntryPoint.Inputs(
|
val inputs = ShowLocationEntryPoint.Inputs(
|
||||||
location = Location(37.4219983, -122.084, 10f),
|
mode = ShowLocationMode.Static(
|
||||||
description = "My location",
|
location = Location(37.4219983, -122.084, 10f),
|
||||||
|
senderName = "Alice",
|
||||||
|
senderId = UserId("@alice:matrix.org"),
|
||||||
|
senderAvatarUrl = null,
|
||||||
|
timestamp = System.currentTimeMillis(),
|
||||||
|
assetType = null,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
val result = entryPoint.createNode(
|
val result = entryPoint.createNode(
|
||||||
parentNode = parentNode,
|
parentNode = parentNode,
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
|
import io.element.android.tests.testutils.test
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
|
@ -66,9 +67,8 @@ class ShowLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter().present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.hasLocationPermission).isFalse()
|
assertThat(initialState.hasLocationPermission).isFalse()
|
||||||
assertThat(initialState.isTrackMyLocation).isFalse()
|
assertThat(initialState.isTrackMyLocation).isFalse()
|
||||||
|
|
@ -84,9 +84,8 @@ class ShowLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter().present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.hasLocationPermission).isFalse()
|
assertThat(initialState.hasLocationPermission).isFalse()
|
||||||
assertThat(initialState.isTrackMyLocation).isFalse()
|
assertThat(initialState.isTrackMyLocation).isFalse()
|
||||||
|
|
@ -97,9 +96,8 @@ class ShowLocationPresenterTest {
|
||||||
fun `emits initial state with location permission`() = runTest {
|
fun `emits initial state with location permission`() = runTest {
|
||||||
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter().present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.hasLocationPermission).isTrue()
|
assertThat(initialState.hasLocationPermission).isTrue()
|
||||||
assertThat(initialState.isTrackMyLocation).isFalse()
|
assertThat(initialState.isTrackMyLocation).isFalse()
|
||||||
|
|
@ -110,9 +108,8 @@ class ShowLocationPresenterTest {
|
||||||
fun `emits initial state with partial location permission`() = runTest {
|
fun `emits initial state with partial location permission`() = runTest {
|
||||||
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.SomeGranted))
|
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.SomeGranted))
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter().present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.hasLocationPermission).isTrue()
|
assertThat(initialState.hasLocationPermission).isTrue()
|
||||||
assertThat(initialState.isTrackMyLocation).isFalse()
|
assertThat(initialState.isTrackMyLocation).isFalse()
|
||||||
|
|
@ -121,9 +118,8 @@ class ShowLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `uses action to share location`() = runTest {
|
fun `uses action to share location`() = runTest {
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter().present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
initialState.eventSink(ShowLocationEvents.Share(location))
|
initialState.eventSink(ShowLocationEvents.Share(location))
|
||||||
|
|
||||||
|
|
@ -135,9 +131,8 @@ class ShowLocationPresenterTest {
|
||||||
fun `centers on user location`() = runTest {
|
fun `centers on user location`() = runTest {
|
||||||
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter().present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.hasLocationPermission).isTrue()
|
assertThat(initialState.hasLocationPermission).isTrue()
|
||||||
assertThat(initialState.isTrackMyLocation).isFalse()
|
assertThat(initialState.isTrackMyLocation).isFalse()
|
||||||
|
|
@ -168,9 +163,8 @@ class ShowLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter().present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
|
|
@ -198,10 +192,8 @@ class ShowLocationPresenterTest {
|
||||||
shouldShowRationale = true,
|
shouldShowRationale = true,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
val presenter = createShowLocationPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
presenter.test {
|
||||||
createShowLocationPresenter().present()
|
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
|
|
@ -227,9 +219,8 @@ class ShowLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter().present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
|
|
@ -258,9 +249,8 @@ class ShowLocationPresenterTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter().present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
|
|
@ -291,9 +281,8 @@ class ShowLocationPresenterTest {
|
||||||
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
||||||
fakeLocationActions.givenLocationEnabled(false)
|
fakeLocationActions.givenLocationEnabled(false)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter(locationActions = fakeLocationActions).present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.hasLocationPermission).isTrue()
|
assertThat(initialState.hasLocationPermission).isTrue()
|
||||||
|
|
||||||
|
|
@ -311,9 +300,8 @@ class ShowLocationPresenterTest {
|
||||||
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
||||||
fakeLocationActions.givenLocationEnabled(false)
|
fakeLocationActions.givenLocationEnabled(false)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
val presenter = createShowLocationPresenter()
|
||||||
createShowLocationPresenter(locationActions = fakeLocationActions).present()
|
presenter.test {
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
initialState.eventSink(ShowLocationEvents.TrackMyLocation(true))
|
initialState.eventSink(ShowLocationEvents.TrackMyLocation(true))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue