diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStateProvider.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStateProvider.kt new file mode 100644 index 0000000000..2af4c41abc --- /dev/null +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/direct/DirectLogoutStateProvider.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 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.logout.api.direct + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncAction + +open class DirectLogoutStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aDirectLogoutState(), + aDirectLogoutState(logoutAction = AsyncAction.Confirming), + aDirectLogoutState(logoutAction = AsyncAction.Loading), + aDirectLogoutState(logoutAction = AsyncAction.Failure(Exception("Error"))), + aDirectLogoutState(logoutAction = AsyncAction.Success("success")), + ) +} + +fun aDirectLogoutState( + canDoDirectSignOut: Boolean = true, + logoutAction: AsyncAction = AsyncAction.Uninitialized, + eventSink: (DirectLogoutEvents) -> Unit = {}, +) = DirectLogoutState( + canDoDirectSignOut = canDoDirectSignOut, + logoutAction = logoutAction, + eventSink = eventSink, +) diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt index 64935f8cda..0cc5cfe476 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutView.kt @@ -17,11 +17,15 @@ package io.element.android.features.logout.impl.direct import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.PreviewParameter import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.logout.api.direct.DirectLogoutEvents import io.element.android.features.logout.api.direct.DirectLogoutState +import io.element.android.features.logout.api.direct.DirectLogoutStateProvider import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.features.logout.impl.ui.LogoutActionDialog +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.di.SessionScope import javax.inject.Inject @@ -50,3 +54,14 @@ class DefaultDirectLogoutView @Inject constructor() : DirectLogoutView { ) } } + +@PreviewsDayNight +@Composable +internal fun DefaultDirectLogoutViewPreview( + @PreviewParameter(DirectLogoutStateProvider::class) state: DirectLogoutState, +) = ElementPreview { + DefaultDirectLogoutView().Render( + state = state, + onSuccessLogout = {}, + ) +} diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt index 78cc65b117..8ebe6c175b 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt @@ -17,6 +17,7 @@ package io.element.android.features.logout.impl import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction @@ -32,6 +33,7 @@ import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.pressTag import org.junit.Rule import org.junit.Test +import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @@ -41,16 +43,11 @@ class LogoutViewTest { @Test fun `clicking on logout sends a LogoutEvents`() { val eventsRecorder = EventsRecorder() - rule.setContent { - LogoutView( - aLogoutState( - eventSink = eventsRecorder - ), - onChangeRecoveryKeyClicked = EnsureNeverCalled(), - onBackClicked = EnsureNeverCalled(), - onSuccessLogout = EnsureNeverCalledWithParam(), - ) - } + rule.setLogoutView( + aLogoutState( + eventSink = eventsRecorder + ), + ) rule.clickOn(CommonStrings.action_signout) eventsRecorder.assertSingle(LogoutEvents.Logout(false)) } @@ -58,17 +55,12 @@ class LogoutViewTest { @Test fun `confirming logout sends a LogoutEvents`() { val eventsRecorder = EventsRecorder() - rule.setContent { - LogoutView( - aLogoutState( - logoutAction = AsyncAction.Confirming, - eventSink = eventsRecorder - ), - onChangeRecoveryKeyClicked = EnsureNeverCalled(), - onBackClicked = EnsureNeverCalled(), - onSuccessLogout = EnsureNeverCalledWithParam(), - ) - } + rule.setLogoutView( + aLogoutState( + logoutAction = AsyncAction.Confirming, + eventSink = eventsRecorder + ), + ) rule.pressTag(TestTags.dialogPositive.value) eventsRecorder.assertSingle(LogoutEvents.Logout(false)) } @@ -77,16 +69,12 @@ class LogoutViewTest { fun `clicking on back invoke back callback`() { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setContent { - LogoutView( - aLogoutState( - eventSink = eventsRecorder - ), - onChangeRecoveryKeyClicked = EnsureNeverCalled(), - onBackClicked = callback, - onSuccessLogout = EnsureNeverCalledWithParam(), - ) - } + rule.setLogoutView( + aLogoutState( + eventSink = eventsRecorder + ), + onBackClicked = callback, + ) rule.pressBack() } } @@ -94,17 +82,12 @@ class LogoutViewTest { @Test fun `clicking on confirm after error sends a LogoutEvents`() { val eventsRecorder = EventsRecorder() - rule.setContent { - LogoutView( - aLogoutState( - logoutAction = AsyncAction.Failure(Exception("Failed to logout")), - eventSink = eventsRecorder - ), - onChangeRecoveryKeyClicked = EnsureNeverCalled(), - onBackClicked = EnsureNeverCalled(), - onSuccessLogout = EnsureNeverCalledWithParam(), - ) - } + rule.setLogoutView( + aLogoutState( + logoutAction = AsyncAction.Failure(Exception("Failed to logout")), + eventSink = eventsRecorder + ), + ) rule.clickOn(CommonStrings.action_signout_anyway) eventsRecorder.assertSingle(LogoutEvents.Logout(true)) } @@ -112,17 +95,12 @@ class LogoutViewTest { @Test fun `clicking on cancel after error sends a LogoutEvents`() { val eventsRecorder = EventsRecorder() - rule.setContent { - LogoutView( - aLogoutState( - logoutAction = AsyncAction.Failure(Exception("Failed to logout")), - eventSink = eventsRecorder - ), - onChangeRecoveryKeyClicked = EnsureNeverCalled(), - onBackClicked = EnsureNeverCalled(), - onSuccessLogout = EnsureNeverCalledWithParam(), - ) - } + rule.setLogoutView( + aLogoutState( + logoutAction = AsyncAction.Failure(Exception("Failed to logout")), + eventSink = eventsRecorder + ), + ) rule.clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(LogoutEvents.CloseDialogs) } @@ -132,17 +110,13 @@ class LogoutViewTest { val data = "data" val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(data) { callback -> - rule.setContent { - LogoutView( - aLogoutState( - logoutAction = AsyncAction.Success(data), - eventSink = eventsRecorder - ), - onChangeRecoveryKeyClicked = EnsureNeverCalled(), - onBackClicked = EnsureNeverCalled(), - onSuccessLogout = callback, - ) - } + rule.setLogoutView( + aLogoutState( + logoutAction = AsyncAction.Success(data), + eventSink = eventsRecorder + ), + onSuccessLogout = callback, + ) } } @@ -150,18 +124,30 @@ class LogoutViewTest { fun `last session setting button invoke onChangeRecoveryKeyClicked`() { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setContent { - LogoutView( - aLogoutState( - isLastDevice = true, - eventSink = eventsRecorder - ), - onChangeRecoveryKeyClicked = callback, - onBackClicked = EnsureNeverCalled(), - onSuccessLogout = EnsureNeverCalledWithParam(), - ) - } + rule.setLogoutView( + aLogoutState( + isLastDevice = true, + eventSink = eventsRecorder + ), + onChangeRecoveryKeyClicked = callback, + ) rule.clickOn(CommonStrings.common_settings) } } } + +private fun AndroidComposeTestRule.setLogoutView( + state: LogoutState, + onChangeRecoveryKeyClicked: () -> Unit = EnsureNeverCalled(), + onBackClicked: () -> Unit = EnsureNeverCalled(), + onSuccessLogout: (logoutUrlResult: String?) -> Unit = EnsureNeverCalledWithParam() +) { + setContent { + LogoutView( + state = state, + onChangeRecoveryKeyClicked = onChangeRecoveryKeyClicked, + onBackClicked = onBackClicked, + onSuccessLogout = onSuccessLogout, + ) + } +} diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt new file mode 100644 index 0000000000..40dc138d46 --- /dev/null +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024 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.logout.impl.direct + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.logout.api.direct.DirectLogoutEvents +import io.element.android.features.logout.api.direct.DirectLogoutState +import io.element.android.features.logout.api.direct.aDirectLogoutState +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureNeverCalledWithParam +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.ensureCalledOnceWithParam +import io.element.android.tests.testutils.pressBackKey +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class DefaultDirectLogoutViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `clicking on confirm logout sends expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setDefaultDirectLogoutView( + state = aDirectLogoutState( + logoutAction = AsyncAction.Confirming, + eventSink = eventsRecorder, + ) + ) + rule.clickOn(CommonStrings.action_signout) + eventsRecorder.assertSingle(DirectLogoutEvents.Logout(false)) + } + + @Test + fun `clicking on cancel logout sends expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setDefaultDirectLogoutView( + state = aDirectLogoutState( + logoutAction = AsyncAction.Confirming, + eventSink = eventsRecorder, + ) + ) + rule.clickOn(CommonStrings.action_cancel) + eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs) + } + + @Ignore("Pressing back key should dismiss the dialog, and so generate the expected event, but it's not the case.") + @Test + fun `clicking on back invoke back callback`() { + val eventsRecorder = EventsRecorder() + rule.setDefaultDirectLogoutView( + state = aDirectLogoutState( + logoutAction = AsyncAction.Confirming, + eventSink = eventsRecorder, + ) + ) + rule.pressBackKey() + eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs) + } + + @Test + fun `clicking on confirm after error sends expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setDefaultDirectLogoutView( + state = aDirectLogoutState( + logoutAction = AsyncAction.Failure(Exception("Error")), + eventSink = eventsRecorder, + ) + ) + rule.clickOn(CommonStrings.action_signout_anyway) + eventsRecorder.assertSingle(DirectLogoutEvents.Logout(true)) + } + + @Test + fun `clicking on cancel after error sends expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setDefaultDirectLogoutView( + state = aDirectLogoutState( + logoutAction = AsyncAction.Failure(Exception("Error")), + eventSink = eventsRecorder, + ) + ) + rule.clickOn(CommonStrings.action_cancel) + eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs) + } + + @Test + fun `success logout invoke expected callback and sends expected Event`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + ensureCalledOnceWithParam(null) { callback -> + rule.setDefaultDirectLogoutView( + state = aDirectLogoutState( + logoutAction = AsyncAction.Success(null), + eventSink = eventsRecorder, + ), + onSuccessLogout = callback + ) + } + } + + @Test + fun `success logout invoke expected callback and sends expected Event with data`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val data = "data" + ensureCalledOnceWithParam(data) { callback -> + rule.setDefaultDirectLogoutView( + state = aDirectLogoutState( + logoutAction = AsyncAction.Success(data), + eventSink = eventsRecorder, + ), + onSuccessLogout = callback + ) + } + } +} + +private fun AndroidComposeTestRule.setDefaultDirectLogoutView( + state: DirectLogoutState, + onSuccessLogout: (String?) -> Unit = EnsureNeverCalledWithParam(), +) { + setContent { + DefaultDirectLogoutView().Render( + state, + onSuccessLogout = onSuccessLogout, + ) + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt index d1a60af615..6f5069f947 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt @@ -16,8 +16,7 @@ package io.element.android.features.preferences.impl.root -import io.element.android.features.logout.api.direct.DirectLogoutState -import io.element.android.libraries.architecture.AsyncAction +import io.element.android.features.logout.api.direct.aDirectLogoutState import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.ui.strings.CommonStrings @@ -37,9 +36,3 @@ fun aPreferencesRootState() = PreferencesRootState( snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete), directLogoutState = aDirectLogoutState(), ) - -fun aDirectLogoutState() = DirectLogoutState( - canDoDirectSignOut = true, - logoutAction = AsyncAction.Uninitialized, - eventSink = {}, -) diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 3427f12577..ce2aed6e70 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -51,6 +51,7 @@ dependencies { implementation(projects.libraries.featureflag.api) implementation(projects.libraries.permissions.api) implementation(projects.libraries.preferences.api) + implementation(projects.libraries.testtags) api(projects.features.roomdetails.api) api(projects.libraries.usersearch.api) api(projects.services.apperror.api) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index 292ad43d0b..1649b15dd2 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -17,7 +17,9 @@ package io.element.android.features.roomdetails.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.features.leaveroom.api.aLeaveRoomState +import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState import io.element.android.features.roomdetails.impl.members.details.aRoomMemberDetailsState import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomMember @@ -29,18 +31,18 @@ open class RoomDetailsStateProvider : PreviewParameterProvider override val values: Sequence get() = sequenceOf( aRoomDetailsState(), - aRoomDetailsState().copy(roomTopic = RoomTopicState.Hidden), - aRoomDetailsState().copy(roomTopic = RoomTopicState.CanAddTopic), - aRoomDetailsState().copy(isEncrypted = false), - aRoomDetailsState().copy(roomAlias = null), - aDmRoomDetailsState().copy(roomName = "Daniel"), - aDmRoomDetailsState(isDmMemberIgnored = true).copy(roomName = "Daniel"), - aRoomDetailsState().copy(canInvite = true), - aRoomDetailsState().copy(isFavorite = true), - aRoomDetailsState().copy( + aRoomDetailsState(roomTopic = RoomTopicState.Hidden), + aRoomDetailsState(roomTopic = RoomTopicState.CanAddTopic), + aRoomDetailsState(isEncrypted = false), + aRoomDetailsState(roomAlias = null), + aDmRoomDetailsState(), + aDmRoomDetailsState(isDmMemberIgnored = true), + aRoomDetailsState(canInvite = true), + aRoomDetailsState(isFavorite = true), + aRoomDetailsState( canEdit = true, // Also test the roomNotificationSettings ALL_MESSAGES in the same screenshot. Icon 'Mute' should be displayed - roomNotificationSettings = RoomNotificationSettings(mode = RoomNotificationMode.ALL_MESSAGES, isDefault = true) + roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.ALL_MESSAGES, isDefault = true) ), // Add other state here ) @@ -68,32 +70,61 @@ fun aDmRoomMember( role = role, ) -fun aRoomDetailsState() = RoomDetailsState( - roomId = "a room id", - roomName = "Marketing", - roomAlias = "#marketing:domain.com", - roomAvatarUrl = null, - roomTopic = RoomTopicState.ExistingTopic( +fun aRoomDetailsState( + roomId: String = "a room id", + roomName: String = "Marketing", + roomAlias: String? = "#marketing:domain.com", + roomAvatarUrl: String? = null, + roomTopic: RoomTopicState = RoomTopicState.ExistingTopic( "Welcome to #marketing, home of the Marketing team " + "|| WIKI PAGE: https://domain.org/wiki/Marketing " + "|| MAIL iki/Marketing " + "|| MAI iki/Marketing " + "|| MAI iki/Marketing..." ), - memberCount = 32, - isEncrypted = true, - canInvite = false, - canEdit = false, - canShowNotificationSettings = true, - roomType = RoomDetailsType.Room, - roomMemberDetailsState = null, - leaveRoomState = aLeaveRoomState(), - roomNotificationSettings = RoomNotificationSettings(mode = RoomNotificationMode.MUTE, isDefault = false), - isFavorite = false, - eventSink = {} + memberCount: Long = 32, + isEncrypted: Boolean = true, + canInvite: Boolean = false, + canEdit: Boolean = false, + canShowNotificationSettings: Boolean = true, + roomType: RoomDetailsType = RoomDetailsType.Room, + roomMemberDetailsState: RoomMemberDetailsState? = null, + leaveRoomState: LeaveRoomState = aLeaveRoomState(), + roomNotificationSettings: RoomNotificationSettings = aRoomNotificationSettings(), + isFavorite: Boolean = false, + eventSink: (RoomDetailsEvent) -> Unit = {}, +) = RoomDetailsState( + roomId = roomId, + roomName = roomName, + roomAlias = roomAlias, + roomAvatarUrl = roomAvatarUrl, + roomTopic = roomTopic, + memberCount = memberCount, + isEncrypted = isEncrypted, + canInvite = canInvite, + canEdit = canEdit, + canShowNotificationSettings = canShowNotificationSettings, + roomType = roomType, + roomMemberDetailsState = roomMemberDetailsState, + leaveRoomState = leaveRoomState, + roomNotificationSettings = roomNotificationSettings, + isFavorite = isFavorite, + eventSink = eventSink ) -fun aDmRoomDetailsState(isDmMemberIgnored: Boolean = false) = aRoomDetailsState().copy( +fun aRoomNotificationSettings( + mode: RoomNotificationMode = RoomNotificationMode.MUTE, + isDefault: Boolean = false, +) = RoomNotificationSettings( + mode = mode, + isDefault = isDefault +) + +fun aDmRoomDetailsState( + isDmMemberIgnored: Boolean = false, + roomName: String = "Daniel", +) = aRoomDetailsState( + roomName = roomName, roomType = RoomDetailsType.Dm(aDmRoomMember(isIgnored = isDmMemberIgnored)), roomMemberDetailsState = aRoomMemberDetailsState() ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index efa8e6e321..da85f5d980 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -53,6 +53,7 @@ import io.element.android.features.roomdetails.impl.blockuser.BlockUserDialogs import io.element.android.features.roomdetails.impl.blockuser.BlockUserSection import io.element.android.features.roomdetails.impl.members.details.RoomMemberHeaderSection import io.element.android.features.roomdetails.impl.members.details.RoomMemberMainActionsSection +import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage import io.element.android.libraries.designsystem.components.ClickableLinkText import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -80,6 +81,8 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.getBestName +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -221,7 +224,7 @@ private fun RoomDetailsTopBar( actions = { if (showEdit) { IconButton(onClick = { showMenu = !showMenu }) { - Icon(Icons.Default.MoreVert, "") + Icon(Icons.Default.MoreVert, stringResource(id = CommonStrings.a11y_user_menu)) } DropdownMenu( expanded = showMenu, @@ -299,6 +302,7 @@ private fun RoomHeaderSection( modifier = Modifier .size(70.dp) .clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) } + .testTag(TestTags.roomDetailAvatar) ) Spacer(modifier = Modifier.height(24.dp)) Text( @@ -463,6 +467,7 @@ internal fun RoomDetailsPreview(@PreviewParameter(RoomDetailsStateProvider::clas internal fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = ElementPreviewDark { ContentToPreview(state) } +@ExcludeFromCoverage @Composable private fun ContentToPreview(state: RoomDetailsState) { RoomDetailsView( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt index a5cc975b48..5e6ed6a7e7 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberHeaderSection.kt @@ -37,6 +37,8 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.testtags.testTag @Composable fun RoomMemberHeaderSection( @@ -53,6 +55,7 @@ fun RoomMemberHeaderSection( modifier = Modifier .clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) } .fillMaxSize() + .testTag(TestTags.memberDetailAvatar) ) } Spacer(modifier = Modifier.height(24.dp)) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt index 7a1cc6c63e..816142b531 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt @@ -17,10 +17,10 @@ package io.element.android.features.roomdetails.impl.notificationsettings import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.roomdetails.impl.aRoomNotificationSettings import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.matrix.api.room.RoomNotificationSettings internal class RoomNotificationSettingsStateProvider : PreviewParameterProvider { override val values: Sequence @@ -43,7 +43,7 @@ internal class RoomNotificationSettingsStateProvider : PreviewParameterProvider< return RoomNotificationSettingsState( showUserDefinedSettingStyle = false, roomName = "Room 1", - AsyncData.Success(RoomNotificationSettings( + AsyncData.Success(aRoomNotificationSettings( mode = RoomNotificationMode.MUTE, isDefault = isDefault )), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt index bb037c2ab5..f30721ed5a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsStateProvider.kt @@ -17,10 +17,10 @@ package io.element.android.features.roomdetails.impl.notificationsettings import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.roomdetails.impl.aRoomNotificationSettings import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.matrix.api.room.RoomNotificationSettings internal class UserDefinedRoomNotificationSettingsStateProvider : PreviewParameterProvider { override val values: Sequence @@ -29,7 +29,7 @@ internal class UserDefinedRoomNotificationSettingsStateProvider : PreviewParamet showUserDefinedSettingStyle = false, roomName = "Room 1", AsyncData.Success( - RoomNotificationSettings( + aRoomNotificationSettings( mode = RoomNotificationMode.MUTE, isDefault = false ) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt new file mode 100644 index 0000000000..e51e3459d6 --- /dev/null +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2024 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 + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.RoomNotificationMode +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureCalledOnceWithTwoParams +import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.EnsureNeverCalledWithParam +import io.element.android.tests.testutils.EnsureNeverCalledWithTwoParams +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.ensureCalledOnceWithParam +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 +import org.robolectric.annotation.Config + +@RunWith(AndroidJUnit4::class) +class RoomDetailsViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `click on back invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + goBack = callback, + ) + rule.pressBack() + } + } + + @Test + fun `click on share invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + onShareRoom = callback, + ) + rule.clickOn(R.string.screen_room_details_share_room_title) + } + } + + @Test + fun `click on share member invokes expected callback`() { + val state = aDmRoomDetailsState() + val roomMember = (state.roomType as RoomDetailsType.Dm).roomMember + ensureCalledOnceWithParam(roomMember) { callback -> + rule.setRoomDetailView( + state = aDmRoomDetailsState(), + onShareMember = callback, + ) + rule.clickOn(CommonStrings.action_share) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on room members invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + openRoomMemberList = callback, + ) + rule.clickOn(CommonStrings.common_people) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on polls invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + openPollHistory = callback, + ) + rule.clickOn(R.string.screen_polls_history_title) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on notification invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + openRoomNotificationSettings = callback, + ) + rule.clickOn(R.string.screen_room_details_notification_title) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on invite people invokes expected callback`() { + ensureCalledOnce { callback -> + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = EventsRecorder(expectEvents = false), + canInvite = true, + ), + invitePeople = callback, + ) + rule.clickOn(R.string.screen_room_details_invite_people_title) + } + } + + @Test + fun `click on add topic emit expected event`() { + ensureCalledOnceWithParam(RoomDetailsAction.AddTopic) { callback -> + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = EventsRecorder(expectEvents = false), + roomTopic = RoomTopicState.CanAddTopic, + ), + onActionClicked = callback, + ) + rule.clickOn(R.string.screen_room_details_add_topic_title) + } + } + + @Test + fun `click on menu edit emit expected event`() { + ensureCalledOnceWithParam(RoomDetailsAction.Edit) { callback -> + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = EventsRecorder(expectEvents = false), + canEdit = true, + ), + onActionClicked = callback, + ) + val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu) + rule.onNodeWithContentDescription(menuContentDescription).performClick() + rule.clickOn(CommonStrings.action_edit) + } + } + + @Test + fun `click on avatar test`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aRoomDetailsState( + eventSink = eventsRecorder, + roomAvatarUrl = "an_avatar_url", + ) + val callback = EnsureCalledOnceWithTwoParams(state.roomName, "an_avatar_url") + rule.setRoomDetailView( + state = state, + openAvatarPreview = callback, + ) + rule.onNodeWithTag(TestTags.roomDetailAvatar.value).performClick() + callback.assertSuccess() + } + + @Test + fun `click on avatar test on DM`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aRoomDetailsState( + roomType = RoomDetailsType.Dm(aDmRoomMember(avatarUrl = "an_avatar_url")), + eventSink = eventsRecorder, + ) + val callback = EnsureCalledOnceWithTwoParams("Daniel", "an_avatar_url") + rule.setRoomDetailView( + state = state, + openAvatarPreview = callback, + ) + rule.onNodeWithTag(TestTags.memberDetailAvatar.value).performClick() + callback.assertSuccess() + } + + @Test + fun `click on mute emit expected event`() { + val eventsRecorder = EventsRecorder() + val state = aRoomDetailsState( + eventSink = eventsRecorder, + roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.ALL_MESSAGES), + ) + rule.setRoomDetailView( + state = state, + ) + rule.clickOn(CommonStrings.common_mute) + eventsRecorder.assertSingle(RoomDetailsEvent.MuteNotification) + } + + @Test + fun `click on unmute emit expected event`() { + val eventsRecorder = EventsRecorder() + val state = aRoomDetailsState( + eventSink = eventsRecorder, + roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.MUTE), + ) + rule.setRoomDetailView( + state = state, + ) + rule.clickOn(CommonStrings.common_unmute) + eventsRecorder.assertSingle(RoomDetailsEvent.UnmuteNotification) + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on favorite emit expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = eventsRecorder, + ), + ) + rule.clickOn(CommonStrings.common_favourite) + eventsRecorder.assertSingle(RoomDetailsEvent.SetFavorite(true)) + } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on leave emit expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = eventsRecorder, + ), + ) + rule.clickOn(R.string.screen_room_details_leave_room_title) + eventsRecorder.assertSingle(RoomDetailsEvent.LeaveRoom) + } +} + +private fun AndroidComposeTestRule.setRoomDetailView( + state: RoomDetailsState = aRoomDetailsState( + eventSink = EventsRecorder(expectEvents = false), + ), + goBack: () -> Unit = EnsureNeverCalled(), + onActionClicked: (RoomDetailsAction) -> Unit = EnsureNeverCalledWithParam(), + onShareRoom: () -> Unit = EnsureNeverCalled(), + onShareMember: (RoomMember) -> Unit = EnsureNeverCalledWithParam(), + openRoomMemberList: () -> Unit = EnsureNeverCalled(), + openRoomNotificationSettings: () -> Unit = EnsureNeverCalled(), + invitePeople: () -> Unit = EnsureNeverCalled(), + openAvatarPreview: (name: String, url: String) -> Unit = EnsureNeverCalledWithTwoParams(), + openPollHistory: () -> Unit = EnsureNeverCalled(), +) { + setContent { + RoomDetailsView( + state = state, + goBack = goBack, + onActionClicked = onActionClicked, + onShareRoom = onShareRoom, + onShareMember = onShareMember, + openRoomMemberList = openRoomMemberList, + openRoomNotificationSettings = openRoomNotificationSettings, + invitePeople = invitePeople, + openAvatarPreview = openAvatarPreview, + openPollHistory = openPollHistory, + ) + } +} diff --git a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt index 7992ddee17..69e073ec07 100644 --- a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt +++ b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt @@ -48,6 +48,16 @@ object TestTags { */ val homeScreenSettings = TestTag("home_screen-settings") + /** + * Room detail screen. + */ + val roomDetailAvatar = TestTag("room_detail-avatar") + + /** + * Room member screen. + */ + val memberDetailAvatar = TestTag("member_detail-avatar") + /** * Welcome screen. */ diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt index 2ea4e44c75..9789aff870 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureCalledOnce.kt @@ -55,6 +55,25 @@ class EnsureCalledOnceWithParam( } } +class EnsureCalledOnceWithTwoParams( + private val expectedParam1: T, + private val expectedParam2: U, +) : (T, U) -> Unit { + private var counter = 0 + override fun invoke(p1: T, p2: U) { + if (p1 != expectedParam1 || p2 != expectedParam2) { + throw AssertionError("Expected to be called with $expectedParam1 and $expectedParam2, but was called with $p1 and $p2") + } + counter++ + } + + fun assertSuccess() { + if (counter != 1) { + throw AssertionError("Expected to be called once, but was called $counter times") + } + } +} + /** * Shortcut for [ ensureCalledOnceWithParam] with Unit result. */ diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..665c8811ac --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b +size 4457 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..73b95f634c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5951e2e78e261693bdcc71fb1cd205082589fda4b5a73196c6ca3f25c02bf502 +size 20332 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5b6c70681b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f5ff11049c6b3fd510015a17fa5e2e016a0cc715f3d4f68850771a20583dbe0 +size 13185 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..98f9608fe1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c79bb3727271a550fdaf7a16d3189e707a578781bc3f77af660e9c935444a6e +size 18862 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..665c8811ac --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Day-1_2_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b +size 4457 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fae8a6fca3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c89ac73df77c2bccb0c2aa80cee1420f78e7d07f0eda89a90bffef55e8cf753 +size 4464 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a7b14f4fb1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bea5d8cc4a49aa2ef9ce3b40b716ce448ce8b3afbdb3a8344ee7e8de9e03fbd8 +size 16895 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..20fff8ae40 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cbceba31af038cb3bed88419a425b2e54b72934969f11c3c1e5901780b93a08 +size 10768 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..44ca48cdb6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee8cef67513d24b7d8a2d8e0098a63ab3dfc891adc0f1aed43aa05a9a6c5de84 +size 15528 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fae8a6fca3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.logout.impl.direct_DefaultDirectLogoutView_null_DefaultDirectLogoutView-Night-1_3_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c89ac73df77c2bccb0c2aa80cee1420f78e7d07f0eda89a90bffef55e8cf753 +size 4464