Fix tests and lint

This commit is contained in:
David Langley 2023-10-19 16:17:57 +01:00
parent b5ca65ed0f
commit 7505ac8eda
10 changed files with 190 additions and 61 deletions

View file

@ -23,7 +23,7 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
open class EditDefaultNotificationSettingsStateProvider: PreviewParameterProvider<EditDefaultNotificationSettingState> {
open class EditDefaultNotificationSettingStateProvider: PreviewParameterProvider<EditDefaultNotificationSettingState> {
override val values: Sequence<EditDefaultNotificationSettingState>
get() = sequenceOf(
anEditDefaultNotificationSettingsState(),

View file

@ -136,7 +136,7 @@ fun EditDefaultNotificationSettingView(
@PreviewsDayNight
@Composable
internal fun EditDefaultNotificationSettingViewPreview(
@PreviewParameter(EditDefaultNotificationSettingsStateProvider::class) state: EditDefaultNotificationSettingState
@PreviewParameter(EditDefaultNotificationSettingStateProvider::class) state: EditDefaultNotificationSettingState
) = ElementPreview {
EditDefaultNotificationSettingView(
state = state,

View file

@ -38,7 +38,7 @@ class EditDefaultNotificationSettingsPresenterTests {
@Test
fun `present - ensures initial state is correct`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService()
val presenter = createPresenter(notificationSettingsService)
val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@ -64,7 +64,7 @@ class EditDefaultNotificationSettingsPresenterTests {
givenGetRoomResult(A_ROOM_ID, room)
}
val roomListService = FakeRoomListService()
val presenter = createPresenter(notificationSettingsService, roomListService, matrixClient)
val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService, roomListService, matrixClient)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@ -78,7 +78,7 @@ class EditDefaultNotificationSettingsPresenterTests {
@Test
fun `present - edit default notification setting`() = runTest {
val presenter = createPresenter()
val presenter = createEditDefaultNotificationSettingPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@ -90,7 +90,7 @@ class EditDefaultNotificationSettingsPresenterTests {
}
}
private fun createPresenter(
private fun createEditDefaultNotificationSettingPresenter(
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
roomListService: FakeRoomListService = FakeRoomListService(),
matrixClient: FakeMatrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService)

View file

@ -0,0 +1,43 @@
/*
* 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.roomdetails.impl.notificationsettings
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
@Composable
fun RoomNotificationSettingsOptions(
selected: RoomNotificationMode?,
enabled: Boolean,
modifier: Modifier = Modifier,
onOptionSelected: (RoomNotificationSettingsItem) -> Unit = {},
) {
val items = roomNotificationSettingsItems()
Column(modifier = modifier.selectableGroup()) {
items.forEach { item ->
RoomNotificationSettingsOption(
roomNotificationSettingsItem = item,
isSelected = selected == item.mode,
onOptionSelected = onOptionSelected,
enabled = enabled
)
}
}
}

View file

@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@ -33,12 +32,11 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.bool.orTrue
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
@ -146,35 +144,6 @@ private fun RoomNotificationSettingsTopBar(
)
}
@Composable
fun RoomNotificationSettingsOptions(
selected: RoomNotificationMode?,
enabled: Boolean,
modifier: Modifier = Modifier,
onOptionSelected: (RoomNotificationSettingsItem) -> Unit = {},
) {
val items = roomNotificationSettingsItems()
Column(modifier = modifier.selectableGroup()) {
items.forEach { item ->
RoomNotificationSettingsOption(
roomNotificationSettingsItem = item,
isSelected = selected == item.mode,
onOptionSelected = onOptionSelected,
enabled = enabled
)
}
}
}
@Composable
fun ShowChangeNotificationSettingError(state: RoomNotificationSettingsState, event: RoomNotificationSettingsEvents) {
ErrorDialog(
title = stringResource(CommonStrings.dialog_title_error),
content = stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode),
onDismiss = { state.eventSink(event) },
)
}
@PreviewsDayNight
@Composable
internal fun RoomNotificationSettingsPreview(

View file

@ -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.
*/
package io.element.android.features.roomdetails.impl.notificationsettings
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun ShowChangeNotificationSettingError(state: RoomNotificationSettingsState, event: RoomNotificationSettingsEvents) {
ErrorDialog(
title = stringResource(CommonStrings.dialog_title_error),
content = stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode),
onDismiss = { state.eventSink(event) },
)
}

View file

@ -0,0 +1,42 @@
/*
* 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.roomdetails.impl.notificationsettings
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
internal class UserDefinedRoomNotificationSettingsStateProvider : PreviewParameterProvider<RoomNotificationSettingsState> {
override val values: Sequence<RoomNotificationSettingsState>
get() = sequenceOf(
RoomNotificationSettingsState(
roomName = "Room 1",
Async.Success(
RoomNotificationSettings(
mode = RoomNotificationMode.MUTE,
isDefault = false)
),
pendingRoomNotificationMode = null,
pendingSetDefault = null,
defaultRoomNotificationMode = RoomNotificationMode.ALL_MESSAGES,
setNotificationSettingAction = Async.Uninitialized,
restoreDefaultAction = Async.Uninitialized,
eventSink = { },
),
)
}

View file

@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.features.roomdetails.impl.R
import io.element.android.libraries.architecture.Async
@ -36,6 +37,8 @@ import io.element.android.libraries.core.bool.orTrue
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
@ -113,7 +116,7 @@ fun UserDefinedRoomNotificationSettingsView(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UserDefinedRoomNotificationSettingsTopBar(
private fun UserDefinedRoomNotificationSettingsTopBar(
roomName: String,
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
@ -128,3 +131,11 @@ fun UserDefinedRoomNotificationSettingsTopBar(
navigationIcon = { BackButton(onClick = onBackPressed) },
)
}
@PreviewsDayNight
@Composable
internal fun UserDefinedRoomNotificationSettingsPreview(
@PreviewParameter(UserDefinedRoomNotificationSettingsStateProvider::class) state: RoomNotificationSettingsState
) = ElementPreview {
UserDefinedRoomNotificationSettingsView(state)
}

View file

@ -25,6 +25,7 @@ import io.element.android.features.roomdetails.impl.notificationsettings.RoomNot
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_ID
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
import io.element.android.tests.testutils.consumeItemsUntilPredicate
import kotlinx.coroutines.test.runTest
@ -34,12 +35,12 @@ import kotlin.time.Duration.Companion.milliseconds
class RoomNotificationSettingsPresenterTests {
@Test
fun `present - initial state is created from room info`() = runTest {
val presenter = aNotificationPresenter()
val presenter = createRoomNotificationSettingsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.roomNotificationSettings).isNull()
Truth.assertThat(initialState.roomNotificationSettings.dataOrNull()).isNull()
Truth.assertThat(initialState.defaultRoomNotificationMode).isNull()
cancelAndIgnoreRemainingEvents()
}
@ -47,53 +48,74 @@ class RoomNotificationSettingsPresenterTests {
@Test
fun `present - notification mode changed`() = runTest {
val presenter = aNotificationPresenter()
val presenter = createRoomNotificationSettingsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
awaitItem().eventSink(RoomNotificationSettingsEvents.RoomNotificationModeChanged(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY))
val updatedState = consumeItemsUntilPredicate {
it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
it.roomNotificationSettings.dataOrNull()?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
}.last()
Truth.assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
Truth.assertThat(updatedState.roomNotificationSettings.dataOrNull()?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
cancelAndIgnoreRemainingEvents()
}
}
@Test
fun `present - observe notification mode changed`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService()
val presenter = aNotificationPresenter(notificationSettingsService)
val presenter = createRoomNotificationSettingsPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
notificationSettingsService.setRoomNotificationMode(A_ROOM_ID, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
val updatedState = consumeItemsUntilPredicate() {
it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
val updatedState = consumeItemsUntilPredicate {
it.roomNotificationSettings.dataOrNull()?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
}.last()
Truth.assertThat(updatedState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
Truth.assertThat(updatedState.roomNotificationSettings.dataOrNull()?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
}
}
@Test
fun `present - notification settings set custom`() = runTest {
fun `present - notification settings set custom failed`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService()
val presenter = aNotificationPresenter(notificationSettingsService)
notificationSettingsService.givenSetNotificationModeError(A_THROWABLE)
val presenter = createRoomNotificationSettingsPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
initialState.eventSink(RoomNotificationSettingsEvents.SetNotificationMode(false))
val defaultState = consumeItemsUntilPredicate(timeout = 8000.milliseconds) {
it.roomNotificationSettings?.isDefault == false
val states = consumeItemsUntilPredicate {
it.roomNotificationSettings.dataOrNull()?.isDefault == false
}
states.forEach {
Truth.assertThat(it.roomNotificationSettings.dataOrNull()?.isDefault).isTrue()
Truth.assertThat(it.pendingSetDefault).isNull()
}
}
}
@Test
fun `present - notification settings set custom`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService()
val presenter = createRoomNotificationSettingsPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
initialState.eventSink(RoomNotificationSettingsEvents.SetNotificationMode(false))
val defaultState = consumeItemsUntilPredicate {
it.roomNotificationSettings.dataOrNull()?.isDefault == false
}.last()
Truth.assertThat(defaultState.roomNotificationSettings?.isDefault).isFalse()
Truth.assertThat(defaultState.roomNotificationSettings.dataOrNull()?.isDefault).isFalse()
}
}
@Test
fun `present - notification settings restore default`() = runTest {
val presenter = aNotificationPresenter()
val presenter = createRoomNotificationSettingsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@ -101,13 +123,14 @@ class RoomNotificationSettingsPresenterTests {
initialState.eventSink(RoomNotificationSettingsEvents.RoomNotificationModeChanged(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY))
initialState.eventSink(RoomNotificationSettingsEvents.SetNotificationMode(true))
val defaultState = consumeItemsUntilPredicate(timeout = 2000.milliseconds) {
it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
it.roomNotificationSettings.dataOrNull()?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
}.last()
Truth.assertThat(defaultState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
Truth.assertThat(defaultState.roomNotificationSettings.dataOrNull()?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
cancelAndIgnoreRemainingEvents()
}
}
private fun aNotificationPresenter(
private fun createRoomNotificationSettingsPresenter(
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService()
): RoomNotificationSettingsPresenter{
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)

View file

@ -42,6 +42,7 @@ class FakeNotificationSettingsService(
private var roomNotificationModeIsDefault: Boolean = initialRoomModeIsDefault
private var callNotificationsEnabled = false
private var atRoomNotificationsEnabled = false
private var setNotificationModeError: Throwable? = null
override val notificationSettingsChangeFlow: SharedFlow<Unit>
get() = _notificationSettingsStateFlow
@ -89,10 +90,15 @@ class FakeNotificationSettingsService(
}
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit> {
roomNotificationModeIsDefault = false
roomNotificationMode = mode
_notificationSettingsStateFlow.emit(Unit)
return Result.success(Unit)
val error = setNotificationModeError
return if (error != null) {
Result.failure(error)
} else {
roomNotificationModeIsDefault = false
roomNotificationMode = mode
_notificationSettingsStateFlow.emit(Unit)
Result.success(Unit)
}
}
override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit> {
@ -131,4 +137,8 @@ class FakeNotificationSettingsService(
override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> {
return Result.success(if (roomNotificationModeIsDefault) listOf() else listOf(A_ROOM_ID.value))
}
fun givenSetNotificationModeError(throwable: Throwable?) {
setNotificationModeError = throwable
}
}