From ccd684ea404e6850a94662c813373b1c0fd4ea36 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 13 Sep 2023 12:44:22 +0100 Subject: [PATCH] Add tests, mocks and lint --- features/preferences/impl/build.gradle.kts | 1 + .../preferences/impl/PreferencesFlowNode.kt | 1 - .../NotificationSettingsEvents.kt | 4 - .../notifications/NotificationSettingsNode.kt | 1 - .../NotificationSettingsPresenter.kt | 50 ++--- .../NotificationSettingsState.kt | 18 +- .../NotificationSettingsStateProvider.kt | 4 +- .../notifications/NotificationSettingsView.kt | 30 ++- ...EditDefaultNotificationSettingPresenter.kt | 1 - .../EditDefaultNotificationSettingView.kt | 2 +- ...faultNotificationSettingsPresenterTests.kt | 64 ++++++ .../FakeSystemNotificationsEnabledProvider.kt | 23 ++ .../NotificationSettingsPresenterTests.kt | 201 ++++++++++++++++++ .../impl/root/PreferencesRootPresenterTest.kt | 2 + .../RoomNotificationSettingsPresenter.kt | 1 - .../roomdetails/RoomDetailsPresenterTests.kt | 6 +- .../RoomNotificationSettingsPresenterTests.kt | 5 +- .../NotificationSettingsService.kt | 1 - .../RustNotificationSettingsService.kt | 6 +- .../FakeNotificationSettingsService.kt | 77 +++++-- .../matrix/test/room/FakeMatrixRoom.kt | 3 +- libraries/pushstore/test/build.gradle.kts | 31 +++ .../test/userpushstore/FakeUserPushStore.kt | 59 +++++ .../userpushstore/FakeUserPushStoreFactory.kt | 28 +++ 24 files changed, 523 insertions(+), 96 deletions(-) create mode 100644 features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt create mode 100644 features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/FakeSystemNotificationsEnabledProvider.kt create mode 100644 features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt create mode 100644 libraries/pushstore/test/build.gradle.kts create mode 100644 libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt create mode 100644 libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index b868dd6b81..e2df4cfa9a 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { implementation(projects.libraries.featureflag.ui) implementation(projects.libraries.network) implementation(projects.libraries.pushstore.api) + implementation(projects.libraries.pushstore.test) implementation(projects.libraries.testtags) implementation(projects.libraries.uiStrings) implementation(projects.features.rageshake.api) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt index 4f38a26d93..1f3c24108b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt @@ -41,7 +41,6 @@ import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.core.UserId import kotlinx.parcelize.Parcelize @ContributesNode(SessionScope::class) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt index f1ee07f6b1..374b8078ca 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt @@ -16,16 +16,12 @@ package io.element.android.features.preferences.impl.notifications -import io.element.android.libraries.matrix.api.room.RoomNotificationMode - sealed interface NotificationSettingsEvents { data object RefreshSystemNotificationsEnabled : NotificationSettingsEvents data class SetNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents data class SetAtRoomNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents data class SetCallNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents - data class SetDefaultGroupNotificationMode(val mode: RoomNotificationMode) : NotificationSettingsEvents - data class SetDefaultOneToOneNotificationMode(val mode: RoomNotificationMode) : NotificationSettingsEvents data object FixConfigurationMismatch : NotificationSettingsEvents data object ClearConfigurationMismatchError : NotificationSettingsEvents } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt index d7a9532219..14ffe2868f 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt @@ -26,7 +26,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.core.UserId @ContributesNode(SessionScope::class) class NotificationSettingsNode @AssistedInject constructor( diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt index 84d140b470..923415b339 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt @@ -56,8 +56,8 @@ class NotificationSettingsPresenter @Inject constructor( .getNotificationEnabledForDevice() .collectAsState(initial = false) - val matrixSettings: MutableState = remember { - mutableStateOf(NotificationSettingsState.MatrixNotificationSettings.Uninitialized) + val matrixSettings: MutableState = remember { + mutableStateOf(NotificationSettingsState.MatrixSettings.Uninitialized) } LaunchedEffect(Unit) { @@ -69,18 +69,20 @@ class NotificationSettingsPresenter @Inject constructor( when (event) { is NotificationSettingsEvents.SetAtRoomNotificationsEnabled -> localCoroutineScope.setAtRoomNotificationsEnabled(event.enabled) is NotificationSettingsEvents.SetCallNotificationsEnabled -> localCoroutineScope.setCallNotificationsEnabled(event.enabled) - is NotificationSettingsEvents.SetDefaultGroupNotificationMode -> localCoroutineScope.setDefaultGroupNotificationMode(event.mode) - is NotificationSettingsEvents.SetDefaultOneToOneNotificationMode -> localCoroutineScope.setDefaultOneToOneNotificationMode(event.mode) is NotificationSettingsEvents.SetNotificationsEnabled -> localCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled) - NotificationSettingsEvents.ClearConfigurationMismatchError -> matrixSettings.value = NotificationSettingsState.MatrixNotificationSettings.InvalidNotificationSettingsState(fixFailed = false) + NotificationSettingsEvents.ClearConfigurationMismatchError -> { + matrixSettings.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false) + } NotificationSettingsEvents.FixConfigurationMismatch -> localCoroutineScope.fixConfigurationMismatch(matrixSettings) - NotificationSettingsEvents.RefreshSystemNotificationsEnabled -> systemNotificationsEnabled.value = systemNotificationsEnabledProvider.notificationsEnabled() + NotificationSettingsEvents.RefreshSystemNotificationsEnabled -> { + systemNotificationsEnabled.value = systemNotificationsEnabledProvider.notificationsEnabled() + } } } return NotificationSettingsState( - matrixNotificationSettings = matrixSettings.value, - appNotificationSettings = NotificationSettingsState.AppNotificationSettings( + matrixSettings = matrixSettings.value, + appSettings = NotificationSettingsState.AppSettings( systemNotificationsEnabled = systemNotificationsEnabled.value, appNotificationsEnabled = appNotificationsEnabled.value ), @@ -89,7 +91,7 @@ class NotificationSettingsPresenter @Inject constructor( } @OptIn(FlowPreview::class) - private fun CoroutineScope.observeNotificationSettings(target: MutableState) { + private fun CoroutineScope.observeNotificationSettings(target: MutableState) { notificationSettingsService.notificationSettingsChangeFlow .debounce(0.5.seconds) .onEach { @@ -98,7 +100,7 @@ class NotificationSettingsPresenter @Inject constructor( .launchIn(this) } - private fun CoroutineScope.fetchSettings(target: MutableState) = launch { + private fun CoroutineScope.fetchSettings(target: MutableState) = launch { val groupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false).getOrThrow() val encryptedGroupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false).getOrThrow() @@ -106,14 +108,14 @@ class NotificationSettingsPresenter @Inject constructor( val encryptedOneToOneDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = true).getOrThrow() if(groupDefaultMode != encryptedGroupDefaultMode || oneToOneDefaultMode != encryptedOneToOneDefaultMode) { - target.value = NotificationSettingsState.MatrixNotificationSettings.InvalidNotificationSettingsState(fixFailed = false) + target.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false) return@launch } val callNotificationsEnabled = notificationSettingsService.isCallEnabled().getOrThrow() val atRoomNotificationsEnabled = notificationSettingsService.isRoomMentionEnabled().getOrThrow() - target.value = NotificationSettingsState.MatrixNotificationSettings.ValidNotificationSettingsState( + target.value = NotificationSettingsState.MatrixSettings.Valid( atRoomNotificationsEnabled = atRoomNotificationsEnabled, callNotificationsEnabled = callNotificationsEnabled, defaultGroupNotificationMode = encryptedGroupDefaultMode, @@ -121,7 +123,7 @@ class NotificationSettingsPresenter @Inject constructor( ) } - private fun CoroutineScope.fixConfigurationMismatch(target: MutableState) = launch { + private fun CoroutineScope.fixConfigurationMismatch(target: MutableState) = launch { runCatching { val groupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false).getOrThrow() val encryptedGroupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false).getOrThrow() @@ -147,7 +149,7 @@ class NotificationSettingsPresenter @Inject constructor( }.fold( onSuccess = {}, onFailure = { - target.value = NotificationSettingsState.MatrixNotificationSettings.InvalidNotificationSettingsState(fixFailed = true) + target.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = true) } ) } @@ -160,27 +162,7 @@ class NotificationSettingsPresenter @Inject constructor( notificationSettingsService.setCallEnabled(enabled) } - private fun CoroutineScope.setDefaultGroupNotificationMode(mode: RoomNotificationMode) = launch { - notificationSettingsService.setDefaultRoomNotificationMode(false, mode, false) - notificationSettingsService.setDefaultRoomNotificationMode(true, mode, false) - } - - private fun CoroutineScope.setDefaultOneToOneNotificationMode(mode: RoomNotificationMode) = launch { - notificationSettingsService.setDefaultRoomNotificationMode(false, mode, true) - notificationSettingsService.setDefaultRoomNotificationMode(true, mode, true) - } - private fun CoroutineScope.setNotificationsEnabled(userPushStore: UserPushStore, enabled: Boolean) = launch { userPushStore.setNotificationEnabledForDevice(enabled) } - - private fun CoroutineScope.getDefaultRoomNotificationMode(isOneToOne: Boolean, defaultRoomNotificationMode: MutableState) = launch { - val encryptedMode = notificationSettingsService.getDefaultRoomNotificationMode(true, isOneToOne).getOrThrow() - val unencryptedMode = notificationSettingsService.getDefaultRoomNotificationMode(false, isOneToOne).getOrThrow() - if (encryptedMode == unencryptedMode) { - defaultRoomNotificationMode.value - } else { - defaultRoomNotificationMode.value = null - } - } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt index ac3502b89b..cf3cf6e3d0 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt @@ -21,25 +21,25 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode @Immutable data class NotificationSettingsState( - val matrixNotificationSettings: MatrixNotificationSettings, - val appNotificationSettings: AppNotificationSettings, + val matrixSettings: MatrixSettings, + val appSettings: AppSettings, val eventSink: (NotificationSettingsEvents) -> Unit, ) { - sealed interface MatrixNotificationSettings { - data object Uninitialized : MatrixNotificationSettings - data class ValidNotificationSettingsState( + sealed interface MatrixSettings { + data object Uninitialized : MatrixSettings + data class Valid( val atRoomNotificationsEnabled: Boolean, val callNotificationsEnabled: Boolean, val defaultGroupNotificationMode: RoomNotificationMode?, val defaultOneToOneNotificationMode: RoomNotificationMode?, - ) : MatrixNotificationSettings + ) : MatrixSettings - data class InvalidNotificationSettingsState( + data class Invalid( val fixFailed: Boolean - ) : MatrixNotificationSettings + ) : MatrixSettings } - data class AppNotificationSettings( + data class AppSettings( val systemNotificationsEnabled: Boolean, val appNotificationsEnabled: Boolean, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt index a4b9014b0c..1e653c47e0 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt @@ -27,13 +27,13 @@ open class NotificationSettingsStateProvider : PreviewParameterProvider InvalidNotificationSettingsView( - showError = state.matrixNotificationSettings.fixFailed, + when (state.matrixSettings) { + is NotificationSettingsState.MatrixSettings.Invalid -> InvalidNotificationSettingsView( + showError = state.matrixSettings.fixFailed, onContinueClicked = { state.eventSink(NotificationSettingsEvents.FixConfigurationMismatch) }, onDismissError = { state.eventSink(NotificationSettingsEvents.ClearConfigurationMismatchError) }, - modifier = modifier, ) - NotificationSettingsState.MatrixNotificationSettings.Uninitialized -> return@PreferenceView - is NotificationSettingsState.MatrixNotificationSettings.ValidNotificationSettingsState -> NotificationSettingsContentView( - matrixSettings = state.matrixNotificationSettings, - systemSettings = state.appNotificationSettings, + NotificationSettingsState.MatrixSettings.Uninitialized -> return@PreferenceView + is NotificationSettingsState.MatrixSettings.Valid -> NotificationSettingsContentView( + matrixSettings = state.matrixSettings, + systemSettings = state.appSettings, onNotificationsEnabledChanged = { state.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(it))}, onGroupChatsClicked = { onOpenEditDefault(false) }, onDirectChatsClicked = { onOpenEditDefault(true) }, onMentionNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(it)) }, onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(it)) }, - modifier = modifier, ) } } @@ -98,8 +96,8 @@ fun NotificationSettingsView( @Composable private fun NotificationSettingsContentView( - matrixSettings: NotificationSettingsState.MatrixNotificationSettings.ValidNotificationSettingsState, - systemSettings: NotificationSettingsState.AppNotificationSettings, + matrixSettings: NotificationSettingsState.MatrixSettings.Valid, + systemSettings: NotificationSettingsState.AppSettings, onNotificationsEnabledChanged: (Boolean) -> Unit, onGroupChatsClicked: () -> Unit, onDirectChatsClicked: () -> Unit, @@ -252,16 +250,16 @@ private fun ContentToPreview(state: NotificationSettingsState) { @Preview @Composable -internal fun InvalidNotificationSettingsViewightPreview(@PreviewParameter(NotificationSettingsStateProvider::class) state: NotificationSettingsState) = - ElementPreviewLight { InvalidNotificationSettingsContentToPreview(state) } +internal fun InvalidNotificationSettingsViewightPreview() = + ElementPreviewLight { InvalidNotificationSettingsContentToPreview() } @Preview @Composable -internal fun InvalidNotificationSettingsViewDarkPreview(@PreviewParameter(NotificationSettingsStateProvider::class) state: NotificationSettingsState) = - ElementPreviewDark { InvalidNotificationSettingsContentToPreview(state) } +internal fun InvalidNotificationSettingsViewDarkPreview() = + ElementPreviewDark { InvalidNotificationSettingsContentToPreview() } @Composable -private fun InvalidNotificationSettingsContentToPreview(state: NotificationSettingsState) { +private fun InvalidNotificationSettingsContentToPreview() { InvalidNotificationSettingsView( showError = false, onContinueClicked = {}, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt index b146155486..cc15e5bdd8 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt @@ -26,7 +26,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.room.RoomNotificationMode import kotlinx.coroutines.CoroutineScope diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt index 5c00cd49cb..777f07b9c7 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt @@ -55,7 +55,7 @@ fun EditDefaultNotificationSettingView( PreferenceCategory(title = stringResource(id = categoryTitle)) { if (state.mode != null) { - Column(modifier = modifier.selectableGroup()) { + Column(modifier = Modifier.selectableGroup()) { validModes.forEach { item -> DefaultNotificationSettingOption( mode = item, diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt new file mode 100644 index 0000000000..e8c9ff4fe5 --- /dev/null +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.notifications + +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth +import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingPresenter +import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingStateEvents +import io.element.android.libraries.matrix.api.room.RoomNotificationMode +import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService +import io.element.android.tests.testutils.consumeItemsUntilPredicate +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class EditDefaultNotificationSettingsPresenterTests { + @Test + fun `present - ensures initial state is correct`() = runTest { + val notificationSettingsService = FakeNotificationSettingsService() + val presenter = EditDefaultNotificationSettingPresenter(notificationSettingsService = notificationSettingsService, isOneToOne = false) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + Truth.assertThat(initialState.mode).isNull() + Truth.assertThat(initialState.isOneToOne).isFalse() + + val loadedState = consumeItemsUntilPredicate { + it.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY + }.last() + Truth.assertThat(loadedState.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) + } + } + + @Test + fun `present - edit default notification setting`() = runTest { + val notificationSettingsService = FakeNotificationSettingsService() + val presenter = EditDefaultNotificationSettingPresenter(notificationSettingsService = notificationSettingsService, isOneToOne = false) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().eventSink(EditDefaultNotificationSettingStateEvents.SetNotificationMode(RoomNotificationMode.ALL_MESSAGES)) + val loadedState = consumeItemsUntilPredicate { + it.mode == RoomNotificationMode.ALL_MESSAGES + }.last() + Truth.assertThat(loadedState.mode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) + } + } +} diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/FakeSystemNotificationsEnabledProvider.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/FakeSystemNotificationsEnabledProvider.kt new file mode 100644 index 0000000000..1a7c1b5004 --- /dev/null +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/FakeSystemNotificationsEnabledProvider.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.notifications + +class FakeSystemNotificationsEnabledProvider: SystemNotificationsEnabledProvider { + override fun notificationsEnabled(): Boolean { + return true + } +} diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt new file mode 100644 index 0000000000..70d15c7a71 --- /dev/null +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.notifications + +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory +import com.google.common.truth.Truth +import io.element.android.libraries.matrix.api.room.RoomNotificationMode +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService +import io.element.android.tests.testutils.consumeItemsUntilPredicate +import kotlinx.coroutines.test.runTest +import org.junit.Test +import kotlin.time.Duration.Companion.milliseconds + +class NotificationSettingsPresenterTests { + @Test + fun `present - ensures initial state is correct`() = runTest { + val presenter = aNotificationPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + Truth.assertThat(initialState.appSettings.appNotificationsEnabled).isFalse() + Truth.assertThat(initialState.appSettings.systemNotificationsEnabled).isTrue() + Truth.assertThat(initialState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Uninitialized) + + val loadedState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + Truth.assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue() + Truth.assertThat(loadedState.appSettings.systemNotificationsEnabled).isTrue() + val valid = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid + Truth.assertThat(valid?.atRoomNotificationsEnabled).isFalse() + Truth.assertThat(valid?.callNotificationsEnabled).isFalse() + Truth.assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) + Truth.assertThat(valid?.defaultOneToOneNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - default group notification mode changed`() = runTest { + val notificationSettingsService = FakeNotificationSettingsService() + val presenter = aNotificationPresenter(notificationSettingsService) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + + notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES) + notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES) + val updatedState = consumeItemsUntilPredicate { + (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid) + ?.defaultGroupNotificationMode == RoomNotificationMode.ALL_MESSAGES + }.last() + val valid = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid + Truth.assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) + } + } + + @Test + fun `present - notification settings mismatched`() = runTest { + val notificationSettingsService = FakeNotificationSettingsService() + val presenter = aNotificationPresenter(notificationSettingsService) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + + notificationSettingsService.setDefaultRoomNotificationMode( + isEncrypted = true, + isOneToOne = false, + mode = RoomNotificationMode.ALL_MESSAGES + ) + notificationSettingsService.setDefaultRoomNotificationMode( + isEncrypted = false, + isOneToOne = false, + mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY + ) + val updatedState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Invalid + }.last() + Truth.assertThat(updatedState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false)) + } + } + + @Test + fun `present - fix notification settings mismatched`() = runTest { + // Start with a mismatched configuration + val notificationSettingsService = FakeNotificationSettingsService( + initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES, + initialGroupDefaultMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, + initialEncryptedOneToOneDefaultMode = RoomNotificationMode.ALL_MESSAGES, + initialOneToOneDefaultMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY + ) + val presenter = aNotificationPresenter(notificationSettingsService) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(NotificationSettingsEvents.FixConfigurationMismatch) + val fixedState = consumeItemsUntilPredicate(timeout = 2000.milliseconds) { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + + val fixedMatrixState = fixedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid + Truth.assertThat(fixedMatrixState?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) + } + } + + @Test + fun `present - set notifications enabled`() = runTest { + val presenter = aNotificationPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val loadedState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + Truth.assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue() + + loadedState.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(false)) + val updatedState = consumeItemsUntilPredicate { + !it.appSettings.appNotificationsEnabled + }.last() + Truth.assertThat(updatedState.appSettings.appNotificationsEnabled).isFalse() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - set call notifications enabled`() = runTest { + val presenter = aNotificationPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val loadedState = consumeItemsUntilPredicate { + (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == false + }.last() + val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid + Truth.assertThat(validMatrixState?.callNotificationsEnabled).isFalse() + + loadedState.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(true)) + val updatedState = consumeItemsUntilPredicate { + (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == true + }.last() + val updatedMatrixState = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid + Truth.assertThat(updatedMatrixState?.callNotificationsEnabled).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - set atRoom notifications enabled`() = runTest { + val presenter = aNotificationPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val loadedState = consumeItemsUntilPredicate { + (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == false + }.last() + val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid + Truth.assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse() + + loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true)) + val updatedState = consumeItemsUntilPredicate { + (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == true + }.last() + val updatedMatrixState = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid + Truth.assertThat(updatedMatrixState?.atRoomNotificationsEnabled).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + private fun aNotificationPresenter( + notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService() + ) : NotificationSettingsPresenter { + val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService) + return NotificationSettingsPresenter( + notificationSettingsService = notificationSettingsService, + userPushStoreFactory = FakeUserPushStoreFactory(), + matrixClient = matrixClient, + systemNotificationsEnabledProvider = FakeSystemNotificationsEnabledProvider(), + ) + } +} diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt index 5217519071..99f7fc06a2 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt @@ -24,6 +24,7 @@ import io.element.android.features.logout.impl.DefaultLogoutPreferencePresenter import io.element.android.libraries.architecture.Async import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.designsystem.utils.SnackbarDispatcher +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_USER_NAME @@ -53,6 +54,7 @@ class PreferencesRootPresenterTest { BuildType.DEBUG, FakeVersionFormatter(), SnackbarDispatcher(), + FakeFeatureFlagService() ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt index f1993d27b1..a6e2477bcc 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt @@ -35,7 +35,6 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import timber.log.Timber import javax.inject.Inject import kotlin.time.Duration.Companion.seconds diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt index bbd67bd11a..010a335267 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt @@ -385,7 +385,7 @@ class RoomDetailsPresenterTests { @Test fun `present - mute room notifications`() = runTest { val leaveRoomPresenter = LeaveRoomPresenterFake() - val notificationSettingsService = FakeNotificationSettingsService(initialMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) + val notificationSettingsService = FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) val presenter = aRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService) moleculeFlow(RecompositionMode.Immediate) { @@ -404,8 +404,8 @@ class RoomDetailsPresenterTests { fun `present - unmute room notifications`() = runTest { val leaveRoomPresenter = LeaveRoomPresenterFake() val notificationSettingsService = FakeNotificationSettingsService( - initialMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, - initialDefaultMode = RoomNotificationMode.ALL_MESSAGES + initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, + initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES ) val room = aMatrixRoom(notificationSettingsService = notificationSettingsService) val presenter = aRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt index 29bec7f622..a1b7831ce2 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt @@ -24,7 +24,6 @@ import io.element.android.features.roomdetails.aMatrixRoom import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsEvents import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsPresenter import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.matrix.test.A_ROOM_NOTIFICATION_MODE import io.element.android.tests.testutils.consumeItemsUntilPredicate import kotlinx.coroutines.test.runTest import org.junit.Test @@ -67,9 +66,9 @@ class RoomNotificationSettingsPresenterTests { initialState.eventSink(RoomNotificationSettingsEvents.RoomNotificationModeChanged(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)) initialState.eventSink(RoomNotificationSettingsEvents.SetNotificationMode(true)) val defaultState = consumeItemsUntilPredicate { - it.roomNotificationSettings?.mode == A_ROOM_NOTIFICATION_MODE + it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY }.last() - Truth.assertThat(defaultState.roomNotificationSettings?.mode).isEqualTo(A_ROOM_NOTIFICATION_MODE) + Truth.assertThat(defaultState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt index 3e62b0c97f..5a81edb052 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt @@ -21,7 +21,6 @@ import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettin import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.RoomNotificationSettings import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.withContext interface NotificationSettingsService { /** diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt index 7dea15a809..a2fffdbdfb 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt @@ -57,7 +57,11 @@ class RustNotificationSettingsService( notificationSettings.getDefaultRoomNotificationMode(isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::mapMode) } - override suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isOneToOne: Boolean): Result = withContext(dispatchers.io) { + override suspend fun setDefaultRoomNotificationMode( + isEncrypted: Boolean, + mode: RoomNotificationMode, + isOneToOne: Boolean + ): Result = withContext(dispatchers.io) { runCatching { notificationSettings.setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode)) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt index 18c9f00e38..77592d6d1f 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt @@ -25,32 +25,75 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow class FakeNotificationSettingsService( - initialMode: RoomNotificationMode = A_ROOM_NOTIFICATION_MODE, - initialDefaultMode: RoomNotificationMode = A_ROOM_NOTIFICATION_MODE + initialRoomMode: RoomNotificationMode = A_ROOM_NOTIFICATION_MODE, + initialGroupDefaultMode: RoomNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, + initialEncryptedGroupDefaultMode: RoomNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, + initialOneToOneDefaultMode: RoomNotificationMode = RoomNotificationMode.ALL_MESSAGES, + initialEncryptedOneToOneDefaultMode: RoomNotificationMode = RoomNotificationMode.ALL_MESSAGES, ) : NotificationSettingsService { - private var _roomNotificationSettingsStateFlow = MutableStateFlow(Unit) - private var defaultRoomNotificationMode: RoomNotificationMode = initialDefaultMode - private var roomNotificationMode: RoomNotificationMode = initialMode + private var _notificationSettingsStateFlow = MutableStateFlow(Unit) + private var defaultGroupRoomNotificationMode: RoomNotificationMode = initialGroupDefaultMode + private var defaultEncryptedGroupRoomNotificationMode: RoomNotificationMode = initialEncryptedGroupDefaultMode + private var defaultOneToOneRoomNotificationMode: RoomNotificationMode = initialOneToOneDefaultMode + private var defaultEncryptedOneToOneRoomNotificationMode: RoomNotificationMode = initialEncryptedOneToOneDefaultMode + private var roomNotificationMode: RoomNotificationMode = initialRoomMode + private var callNotificationsEnabled = false + private var atRoomNotificationsEnabled = false override val notificationSettingsChangeFlow: SharedFlow - get() = _roomNotificationSettingsStateFlow + get() = _notificationSettingsStateFlow - override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, membersCount: Long): Result { - return Result.success(RoomNotificationSettings(mode = roomNotificationMode, isDefault = roomNotificationMode == defaultRoomNotificationMode)) + override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result { + return Result.success( + RoomNotificationSettings( + mode = roomNotificationMode, + isDefault = roomNotificationMode == defaultEncryptedGroupRoomNotificationMode + ) + ) } - override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, membersCount: Long): Result { - return Result.success(defaultRoomNotificationMode) + override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result { + return if (isOneToOne) { + if (isEncrypted) { + Result.success(defaultEncryptedOneToOneRoomNotificationMode) + } else { + Result.success(defaultOneToOneRoomNotificationMode) + } + } else { + if (isEncrypted) { + Result.success(defaultEncryptedGroupRoomNotificationMode) + } else { + Result.success(defaultGroupRoomNotificationMode) + } + } + } + + override suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isOneToOne: Boolean): Result { + if (isOneToOne) { + if (isEncrypted) { + defaultEncryptedOneToOneRoomNotificationMode = mode + } else { + defaultOneToOneRoomNotificationMode = mode + } + } else { + if (isEncrypted) { + defaultEncryptedGroupRoomNotificationMode = mode + } else { + defaultGroupRoomNotificationMode = mode + } + } + _notificationSettingsStateFlow.emit(Unit) + return Result.success(Unit) } override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result { roomNotificationMode = mode - _roomNotificationSettingsStateFlow.emit(Unit) + _notificationSettingsStateFlow.emit(Unit) return Result.success(Unit) } override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result { - roomNotificationMode = defaultRoomNotificationMode - _roomNotificationSettingsStateFlow.emit(Unit) + roomNotificationMode = defaultEncryptedGroupRoomNotificationMode + _notificationSettingsStateFlow.emit(Unit) return Result.success(Unit) } @@ -58,23 +101,25 @@ class FakeNotificationSettingsService( return setRoomNotificationMode(roomId, RoomNotificationMode.MUTE) } - override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, membersCount: Long): Result { + override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result { return restoreDefaultRoomNotificationMode(roomId) } override suspend fun isRoomMentionEnabled(): Result { - return Result.success(false) + return Result.success(atRoomNotificationsEnabled) } override suspend fun setRoomMentionEnabled(enabled: Boolean): Result { + atRoomNotificationsEnabled = enabled return Result.success(Unit) } override suspend fun isCallEnabled(): Result { - return Result.success(false) + return Result.success(callNotificationsEnabled) } override suspend fun setCallEnabled(enabled: Boolean): Result { + callNotificationsEnabled = enabled return Result.success(Unit) } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index d53b8e2c92..f551a33225 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -72,7 +72,6 @@ class FakeMatrixRoom( private var userAvatarUrlResult = Result.success(null) private var updateMembersResult: Result = Result.success(Unit) private var joinRoomResult = Result.success(Unit) - private var updateRoomNotificationSettingsResult: Result = Result.success(Unit) private var inviteUserResult = Result.success(Unit) private var canInviteResult = Result.success(true) private var canRedactResult = Result.success(canRedact) @@ -149,7 +148,7 @@ class FakeMatrixRoom( } override suspend fun updateRoomNotificationSettings(): Result = simulateLongTask { - val notificationSettings = notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, activeMemberCount).getOrThrow() + val notificationSettings = notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow() roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(notificationSettings) return Result.success(Unit) } diff --git a/libraries/pushstore/test/build.gradle.kts b/libraries/pushstore/test/build.gradle.kts new file mode 100644 index 0000000000..a100c40f7d --- /dev/null +++ b/libraries/pushstore/test/build.gradle.kts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.pushstore.test" +} + +dependencies { + api(projects.libraries.matrix.api) + api(libs.coroutines.core) + implementation(libs.coroutines.test) + implementation(projects.tests.testutils) + implementation(projects.libraries.pushstore.api) +} diff --git a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt new file mode 100644 index 0000000000..c697e0d3c9 --- /dev/null +++ b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.element.android.libraries.pushstore.test.userpushstore + +import io.element.android.libraries.pushstore.api.UserPushStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeUserPushStore: UserPushStore { + + private var pushProviderName: String? = null + private var currentRegisteredPushKey: String? = null + private val notificationEnabledForDevice = MutableStateFlow(true) + override suspend fun getPushProviderName(): String? { + return pushProviderName + } + + override suspend fun setPushProviderName(value: String) { + pushProviderName = value + } + + override suspend fun getCurrentRegisteredPushKey(): String? { + return currentRegisteredPushKey + } + + override suspend fun setCurrentRegisteredPushKey(value: String) { + currentRegisteredPushKey = value + } + + override fun getNotificationEnabledForDevice(): Flow { + return notificationEnabledForDevice + } + + override suspend fun setNotificationEnabledForDevice(enabled: Boolean) { + notificationEnabledForDevice.value = enabled + } + + override fun useCompleteNotificationFormat(): Boolean { + return true + } + + override suspend fun reset() { + + } +} diff --git a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt new file mode 100644 index 0000000000..f51893b00a --- /dev/null +++ b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.element.android.libraries.pushstore.test.userpushstore + +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.pushstore.api.UserPushStore +import io.element.android.libraries.pushstore.api.UserPushStoreFactory + +class FakeUserPushStoreFactory: UserPushStoreFactory { + override fun create(userId: SessionId): UserPushStore { + return FakeUserPushStore() + } +} +