fix(deps): update dependency androidx.compose:compose-bom to v2025.04.01 (#4631)

* fix(deps): update dependency androidx.compose:compose-bom to v2025.04.01

* Fix autofill deprecations

* Adapt our custom BottomSheetState and scaffold to the new APIs

* Get rid of all the custom bottom sheet implementation

It doesn't seem to be needed anymore 🎉

* Replace `semantics { invisibleToUser() }`  with `hideFromAccessibility()`

* Update screenshots

* Add commit and cancel callbacks for autofill on the login view

* Fix broken tests caused mainly by https://issuetracker.google.com/issues/366255137

Add `LocalUiTestMode` composition local and helper functions.

* Remove dependency that caused a new license to need to be approved

* Let setSafeContent handle setting the value for LocalUiTestMode

* Fix broken test

* Apply fix to RoomMemberModerationViewTest and RoomListDeclineInviteMenuTest

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
Co-authored-by: ElementBot <android@element.io>
Co-authored-by: Benoit Marty <benoit@matrix.org>
This commit is contained in:
renovate[bot] 2025-06-03 21:05:43 +00:00 committed by GitHub
parent b59ddeb652
commit 7bb1e24ff5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 189 additions and 1012 deletions

View file

@ -17,6 +17,7 @@ import io.element.android.features.createroom.impl.R
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.setSafeContent
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
@ -56,7 +57,7 @@ class JoinBaseRoomByAddressViewTest {
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setJoinRoomByAddressView(
state: JoinRoomByAddressState,
) {
setContent {
setSafeContent {
JoinRoomByAddressView(state = state)
}
}

View file

@ -34,10 +34,12 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.AutofillType
import androidx.compose.ui.autofill.ContentType
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentType
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
@ -54,7 +56,6 @@ import io.element.android.libraries.designsystem.atomic.organisms.InfoListOrgani
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.components.list.SwitchListItem
import io.element.android.libraries.designsystem.modifiers.autofill
import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -276,14 +277,9 @@ private fun Content(
.fillMaxWidth()
.onTabOrEnterKeyFocusNext(focusManager)
.testTag(TestTags.loginPassword)
.autofill(
autofillTypes = listOf(AutofillType.Password),
onFill = {
val sanitized = it.sanitize()
passwordFieldState = sanitized
eventSink(AccountDeactivationEvents.SetPassword(sanitized))
}
),
.semantics {
contentType = ContentType.Password
},
onValueChange = {
val sanitized = it.sanitize()
passwordFieldState = sanitized

View file

@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.SheetValue
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.material3.rememberStandardBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
@ -46,8 +48,6 @@ import io.element.android.libraries.designsystem.theme.components.FloatingAction
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.theme.components.bottomsheet.rememberBottomSheetScaffoldState
import io.element.android.libraries.designsystem.theme.components.bottomsheet.rememberStandardBottomSheetState
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.maplibre.compose.CameraMode
import io.element.android.libraries.maplibre.compose.CameraMoveStartedReason

View file

@ -7,6 +7,7 @@
package io.element.android.features.login.impl.screens.loginpassword
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -30,10 +31,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.AutofillType
import androidx.compose.ui.autofill.ContentType
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.platform.LocalAutofillManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentType
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
@ -51,7 +55,6 @@ import io.element.android.libraries.designsystem.components.BigIcon
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.form.textFieldState
import io.element.android.libraries.designsystem.modifiers.autofill
import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusNext
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -71,6 +74,13 @@ fun LoginPasswordView(
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val autofillManager = LocalAutofillManager.current
BackHandler {
autofillManager?.cancel()
onBackClick()
}
val isLoading by remember(state.loginAction) {
derivedStateOf {
state.loginAction is AsyncData.Loading
@ -82,6 +92,8 @@ fun LoginPasswordView(
// Clear focus to prevent keyboard issues with textfields
focusManager.clearFocus(force = true)
autofillManager?.commit()
state.eventSink(LoginPasswordEvents.Submit)
}
@ -90,7 +102,12 @@ fun LoginPasswordView(
topBar = {
TopAppBar(
title = {},
navigationIcon = { BackButton(onClick = onBackClick) },
navigationIcon = {
BackButton(onClick = {
autofillManager?.cancel()
onBackClick()
})
},
)
}
) { padding ->
@ -175,14 +192,9 @@ private fun LoginForm(
.fillMaxWidth()
.onTabOrEnterKeyFocusNext(focusManager)
.testTag(TestTags.loginEmailUsername)
.autofill(
autofillTypes = listOf(AutofillType.Username),
onFill = {
val sanitized = it.sanitize()
loginFieldState = sanitized
eventSink(LoginPasswordEvents.SetLogin(sanitized))
}
),
.semantics {
contentType = ContentType.Username
},
placeholder = stringResource(CommonStrings.common_username),
onValueChange = {
val sanitized = it.sanitize()
@ -227,14 +239,9 @@ private fun LoginForm(
.fillMaxWidth()
.onTabOrEnterKeyFocusNext(focusManager)
.testTag(TestTags.loginPassword)
.autofill(
autofillTypes = listOf(AutofillType.Password),
onFill = {
val sanitized = it.sanitize()
passwordFieldState = sanitized
eventSink(LoginPasswordEvents.SetPassword(sanitized))
}
),
.semantics {
contentType = ContentType.Password
},
onValueChange = {
val sanitized = it.sanitize()
passwordFieldState = sanitized

View file

@ -8,17 +8,21 @@
package io.element.android.features.login.impl.screens.loginpassword
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.hasText
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.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.matrix.test.A_PASSWORD
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EnsureNeverCalled
import io.element.android.tests.testutils.EventsRecorder
@ -120,15 +124,15 @@ class LoginPasswordViewTest {
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(A_PASSWORD).assertDoesNotExist()
rule.onNodeWithTag(TestTags.loginPassword.value).assert(hasText("••••••••"))
// Show password
val a11yShowPassword = rule.activity.getString(CommonStrings.a11y_show_password)
rule.onNodeWithContentDescription(a11yShowPassword).performClick()
rule.onNodeWithText(A_PASSWORD).assertExists()
rule.onNodeWithTag(TestTags.loginPassword.value).assert(hasText(A_PASSWORD))
// Hide password
val a11yHidePassword = rule.activity.getString(CommonStrings.a11y_hide_password)
rule.onNodeWithContentDescription(a11yHidePassword).performClick()
rule.onNodeWithText(A_PASSWORD).assertDoesNotExist()
rule.onNodeWithTag(TestTags.loginPassword.value).assert(hasText("••••••••"))
}
@Test

View file

@ -13,6 +13,8 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SheetValue
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.material3.rememberStandardBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
@ -29,10 +31,8 @@ import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.min
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.designsystem.theme.components.BottomSheetScaffold
import io.element.android.libraries.designsystem.theme.components.bottomsheet.CustomSheetState
import io.element.android.libraries.designsystem.theme.components.bottomsheet.rememberBottomSheetScaffoldState
import io.element.android.libraries.designsystem.theme.components.bottomsheet.rememberStandardBottomSheetState
import kotlin.math.roundToInt
/**
@ -139,8 +139,8 @@ internal fun ExpandableBottomSheetScaffold(
modifier = Modifier.fillMaxHeight(),
measurePolicy = { measurables, constraints ->
val constraintHeight = constraints.maxHeight
val offset = scaffoldState.bottomSheetState.getIntOffset() ?: 0
val height = Integer.max(0, constraintHeight - offset)
val offset = tryOrNull { scaffoldState.bottomSheetState.requireOffset() } ?: 0f
val height = Integer.max(0, constraintHeight - offset.roundToInt())
val top = measurables[0].measure(
constraints.copy(
minHeight = height,
@ -165,12 +165,6 @@ internal fun ExpandableBottomSheetScaffold(
)
}
private fun CustomSheetState.getIntOffset(): Int? = try {
requireOffset().roundToInt()
} catch (e: IllegalStateException) {
null
}
private sealed interface Slot {
data class SheetContent(val key: Int?) : Slot
data object DragHandle : Slot

View file

@ -47,6 +47,7 @@ import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.messageFromMeBackground
import io.element.android.libraries.designsystem.theme.messageFromOtherBackground
import io.element.android.libraries.designsystem.utils.LocalUiTestMode
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.utils.time.isTalkbackActive
@ -112,7 +113,9 @@ fun MessageEventBubble(
state.isMine -> ElementTheme.colors.messageFromMeBackground
else -> ElementTheme.colors.messageFromOtherBackground
}
val bubbleShape = bubbleShape()
// If we're running in UI test mode, we want to use a different shape to avoid
// this issue: https://issuetracker.google.com/issues/366255137
val bubbleShape = if (LocalUiTestMode.current) RoundedCornerShape(12.dp) else bubbleShape()
val radiusPx = (avatarRadius + SENDER_AVATAR_BORDER_WIDTH).toPx()
val yOffsetPx = -(NEGATIVE_MARGIN_FOR_BUBBLE + avatarRadius).toPx()
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl

View file

@ -18,7 +18,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@ -80,7 +80,7 @@ fun TimelineEventTimestampView(
.clickable(isVerifiedUserSendFailure) {
eventSink(TimelineEvents.ComputeVerifiedUserSendFailure(event))
}
.semantics { invisibleToUser() }
.semantics { hideFromAccessibility() }
)
}
@ -95,7 +95,7 @@ fun TimelineEventTimestampView(
.clickable {
eventSink(TimelineEvents.ShowShieldDialog(shield))
}
.semantics { invisibleToUser() },
.semantics { hideFromAccessibility() },
tint = shield.toIconColor(),
)
Spacer(modifier = Modifier.width(4.dp))

View file

@ -39,7 +39,7 @@ import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.traversalIndex
@ -439,7 +439,7 @@ private fun MessageSenderInformation(
// Add external clickable modifier with no indicator so the touch target is larger than just the display name
.clickable(onClick = onClick, enabled = true, interactionSource = remember { MutableInteractionSource() }, indication = null)
.clearAndSetSemantics {
invisibleToUser()
hideFromAccessibility()
}
) {
Avatar(

View file

@ -16,7 +16,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
@ -43,7 +43,7 @@ fun TimelineItemReactionsView(
var expanded: Boolean by rememberSaveable { mutableStateOf(false) }
TimelineItemReactionsView(
modifier = modifier.semantics {
invisibleToUser()
hideFromAccessibility()
},
reactions = reactionsState.reactions,
userCanSendReaction = userCanSendReaction,

View file

@ -36,7 +36,7 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@ -136,7 +136,7 @@ fun TimelineItemVideoView(
imageVector = CompoundIcons.PlaySolid(),
contentDescription = stringResource(id = CommonStrings.a11y_play),
colorFilter = ColorFilter.tint(Color.White),
modifier = Modifier.semantics { invisibleToUser() }
modifier = Modifier.semantics { hideFromAccessibility() }
)
}
}

View file

@ -26,7 +26,7 @@ import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@ -59,7 +59,7 @@ fun TimelineItemReadReceiptView(
if (renderReadReceipts) {
ReadReceiptsRow(
modifier = modifier.clearAndSetSemantics {
invisibleToUser()
hideFromAccessibility()
}
) {
ReadReceiptsAvatars(

View file

@ -68,6 +68,7 @@ import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.pressBack
import io.element.android.tests.testutils.setSafeContent
import kotlinx.collections.immutable.persistentListOf
import org.junit.Rule
import org.junit.Test
@ -567,11 +568,9 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMessa
onJoinCallClick: () -> Unit = EnsureNeverCalled(),
onViewAllPinnedMessagesClick: () -> Unit = EnsureNeverCalled(),
) {
setContent {
setSafeContent {
// Cannot use the RichTextEditor, so simulate a LocalInspectionMode
CompositionLocalProvider(
LocalInspectionMode provides true
) {
CompositionLocalProvider(LocalInspectionMode provides true) {
MessagesView(
state = state,
onBackClick = onBackClick,

View file

@ -14,6 +14,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.setSafeContent
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
@ -54,7 +55,7 @@ class ResolveVerifiedUserSendFailureViewTest {
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setResolveVerifiedUserSendFailureView(
state: ResolveVerifiedUserSendFailureState,
) {
setContent {
setSafeContent {
ResolveVerifiedUserSendFailureView(state = state)
}
}

View file

@ -103,7 +103,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setPinne
onLinkClick: (Link) -> Unit = EnsureNeverCalledWithParam(),
onLinkLongClick: (Link) -> Unit = EnsureNeverCalledWithParam(),
) {
setSafeContent {
setSafeContent(clearAndroidUiDispatcher = true) {
PinnedMessagesListView(
state = state,
onBackClick = onBackClick,

View file

@ -187,7 +187,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTimel
onJoinCallClick: () -> Unit = EnsureNeverCalled(),
forceJumpToBottomVisibility: Boolean = false,
) {
setSafeContent {
setSafeContent(clearAndroidUiDispatcher = true) {
TimelineView(
state = state,
timelineProtectionState = timelineProtectionState,

View file

@ -21,6 +21,7 @@ import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.ensureCalledTimes
import io.element.android.tests.testutils.pressBack
import io.element.android.tests.testutils.setSafeContent
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
@ -159,14 +160,14 @@ class RolesAndPermissionsViewTest {
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRolesAndPermissionsView(
state: RolesAndPermissionsState = aRolesAndPermissionsState(
eventSink = EventsRecorder(expectEvents = false),
eventSink = EventsRecorder(expectEvents = false),
),
goBack: () -> Unit = EnsureNeverCalled(),
openAdminList: () -> Unit = EnsureNeverCalled(),
openModeratorList: () -> Unit = EnsureNeverCalled(),
openPermissionScreens: () -> Unit = EnsureNeverCalled(),
) {
setContent {
setSafeContent {
RolesAndPermissionsView(
state = state,
rolesAndPermissionsNavigator = object : RolesAndPermissionsNavigator {

View file

@ -8,6 +8,7 @@
package io.element.android.features.roomlist.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.matrix.api.core.RoomId
@ -16,6 +17,7 @@ import io.element.android.tests.testutils.EnsureCalledOnceWithParam
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.setSafeContent
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@ -28,15 +30,10 @@ class RoomListContextMenuTest {
fun `clicking on Mark as read generates expected Events`() {
val eventsRecorder = EventsRecorder<RoomListEvents>()
val contextMenu = aContextMenuShown(hasNewContent = true)
rule.setContent {
RoomListContextMenu(
contextMenu = contextMenu,
canReportRoom = false,
eventSink = eventsRecorder,
onRoomSettingsClick = EnsureNeverCalledWithParam(),
onReportRoomClick = EnsureNeverCalledWithParam(),
)
}
rule.setRoomListContextMenu(
contextMenu = contextMenu,
eventSink = eventsRecorder,
)
rule.clickOn(R.string.screen_roomlist_mark_as_read)
eventsRecorder.assertList(
listOf(
@ -50,15 +47,10 @@ class RoomListContextMenuTest {
fun `clicking on Mark as unread generates expected Events`() {
val eventsRecorder = EventsRecorder<RoomListEvents>()
val contextMenu = aContextMenuShown(hasNewContent = false)
rule.setContent {
RoomListContextMenu(
contextMenu = contextMenu,
canReportRoom = false,
eventSink = eventsRecorder,
onRoomSettingsClick = EnsureNeverCalledWithParam(),
onReportRoomClick = EnsureNeverCalledWithParam(),
)
}
rule.setRoomListContextMenu(
contextMenu = contextMenu,
eventSink = eventsRecorder,
)
rule.clickOn(R.string.screen_roomlist_mark_as_unread)
eventsRecorder.assertList(
listOf(
@ -72,15 +64,10 @@ class RoomListContextMenuTest {
fun `clicking on Leave room generates expected Events`() {
val eventsRecorder = EventsRecorder<RoomListEvents>()
val contextMenu = aContextMenuShown(isDm = false)
rule.setContent {
RoomListContextMenu(
contextMenu = contextMenu,
canReportRoom = false,
eventSink = eventsRecorder,
onRoomSettingsClick = EnsureNeverCalledWithParam(),
onReportRoomClick = EnsureNeverCalledWithParam(),
)
}
rule.setRoomListContextMenu(
contextMenu = contextMenu,
eventSink = eventsRecorder,
)
rule.clickOn(CommonStrings.action_leave_room)
eventsRecorder.assertList(
listOf(
@ -95,15 +82,13 @@ class RoomListContextMenuTest {
val eventsRecorder = EventsRecorder<RoomListEvents>()
val contextMenu = aContextMenuShown()
val callback = EnsureCalledOnceWithParam(contextMenu.roomId, Unit)
rule.setContent {
RoomListContextMenu(
contextMenu = contextMenu,
canReportRoom = true,
eventSink = eventsRecorder,
onRoomSettingsClick = EnsureNeverCalledWithParam(),
onReportRoomClick = callback,
)
}
rule.setRoomListContextMenu(
contextMenu = contextMenu,
canReportRoom = true,
eventSink = eventsRecorder,
onRoomSettingsClick = EnsureNeverCalledWithParam(),
onReportRoomClick = callback,
)
rule.clickOn(CommonStrings.action_report_room)
eventsRecorder.assertSingle(RoomListEvents.HideContextMenu)
callback.assertSuccess()
@ -114,15 +99,11 @@ class RoomListContextMenuTest {
val eventsRecorder = EventsRecorder<RoomListEvents>()
val contextMenu = aContextMenuShown()
val callback = EnsureCalledOnceWithParam(contextMenu.roomId, Unit)
rule.setContent {
RoomListContextMenu(
contextMenu = contextMenu,
canReportRoom = false,
eventSink = eventsRecorder,
onRoomSettingsClick = callback,
onReportRoomClick = EnsureNeverCalledWithParam(),
)
}
rule.setRoomListContextMenu(
contextMenu = contextMenu,
eventSink = eventsRecorder,
onRoomSettingsClick = callback,
)
rule.clickOn(CommonStrings.common_settings)
eventsRecorder.assertSingle(RoomListEvents.HideContextMenu)
callback.assertSuccess()
@ -133,15 +114,11 @@ class RoomListContextMenuTest {
val eventsRecorder = EventsRecorder<RoomListEvents>()
val contextMenu = aContextMenuShown(isDm = false, isFavorite = false)
val callback = EnsureNeverCalledWithParam<RoomId>()
rule.setContent {
RoomListContextMenu(
contextMenu = contextMenu,
canReportRoom = false,
eventSink = eventsRecorder,
onRoomSettingsClick = callback,
onReportRoomClick = EnsureNeverCalledWithParam(),
)
}
rule.setRoomListContextMenu(
contextMenu = contextMenu,
eventSink = eventsRecorder,
onRoomSettingsClick = callback,
)
rule.clickOn(CommonStrings.common_favourite)
eventsRecorder.assertList(
listOf(
@ -149,4 +126,22 @@ class RoomListContextMenuTest {
)
)
}
private fun AndroidComposeTestRule<*, *>.setRoomListContextMenu(
contextMenu: RoomListState.ContextMenu.Shown,
canReportRoom: Boolean = false,
eventSink: (RoomListEvents) -> Unit,
onRoomSettingsClick: (RoomId) -> Unit = EnsureNeverCalledWithParam(),
onReportRoomClick: (RoomId) -> Unit = EnsureNeverCalledWithParam(),
) {
setSafeContent {
RoomListContextMenu(
contextMenu = contextMenu,
canReportRoom = canReportRoom,
onRoomSettingsClick = onRoomSettingsClick,
onReportRoomClick = onReportRoomClick,
eventSink = eventSink,
)
}
}
}

View file

@ -16,6 +16,7 @@ import io.element.android.tests.testutils.EnsureCalledOnceWithParam
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.setSafeContent
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@ -28,7 +29,7 @@ class RoomListDeclineInviteMenuTest {
fun `clicking on decline emits the expected Events`() {
val eventsRecorder = EventsRecorder<RoomListEvents>()
val menu = RoomListState.DeclineInviteMenu.Shown(roomSummary = aRoomListRoomSummary())
rule.setContent {
rule.setSafeContent {
RoomListDeclineInviteMenu(
menu = menu,
canReportRoom = false,
@ -49,7 +50,7 @@ class RoomListDeclineInviteMenuTest {
fun `clicking on decline and block when canReportRoom=true, it emits the expected Events and callback`() {
val eventsRecorder = EventsRecorder<RoomListEvents>()
val menu = RoomListState.DeclineInviteMenu.Shown(roomSummary = aRoomListRoomSummary())
rule.setContent {
rule.setSafeContent {
RoomListDeclineInviteMenu(
menu = menu,
canReportRoom = true,
@ -66,7 +67,7 @@ class RoomListDeclineInviteMenuTest {
fun `clicking on decline and block when canReportRoom=false, it emits the expected Events`() {
val eventsRecorder = EventsRecorder<RoomListEvents>()
val menu = RoomListState.DeclineInviteMenu.Shown(roomSummary = aRoomListRoomSummary())
rule.setContent {
rule.setSafeContent {
RoomListDeclineInviteMenu(
menu = menu,
canReportRoom = false,
@ -86,7 +87,7 @@ class RoomListDeclineInviteMenuTest {
fun `clicking on cancel emits the expected Event`() {
val eventsRecorder = EventsRecorder<RoomListEvents>()
val menu = RoomListState.DeclineInviteMenu.Shown(roomSummary = aRoomListRoomSummary())
rule.setContent {
rule.setSafeContent {
RoomListDeclineInviteMenu(
menu = menu,
canReportRoom = false,

View file

@ -29,6 +29,7 @@ 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.setSafeContent
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Rule
import org.junit.Test
@ -52,7 +53,7 @@ class RoomListViewTest {
eventsRecorder.assertList(
listOf(
RoomListEvents.UpdateVisibleRange(IntRange.EMPTY),
RoomListEvents.UpdateVisibleRange(0 until 2),
RoomListEvents.UpdateVisibleRange(0..2),
)
)
}
@ -273,7 +274,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomL
onReportRoomClick: (RoomId) -> Unit = EnsureNeverCalledWithParam(),
onDeclineInviteAndBlockUser: (RoomListRoomSummary) -> Unit = EnsureNeverCalledWithParam(),
) {
setContent {
setSafeContent {
RoomListView(
state = state,
onRoomClick = onRoomClick,

View file

@ -22,6 +22,7 @@ import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnceWithTwoParams
import io.element.android.tests.testutils.pressTag
import io.element.android.tests.testutils.setSafeContent
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
@ -217,7 +218,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomM
state: InternalRoomMemberModerationState,
onSelectAction: (ModerationAction, MatrixUser) -> Unit = EnsureNeverCalledWithTwoParams(),
) {
setContent {
setSafeContent {
RoomMemberModerationView(
state = state,
onSelectAction = onSelectAction,

View file

@ -24,10 +24,12 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.AutofillType
import androidx.compose.ui.autofill.ContentType
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentType
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
@ -39,7 +41,6 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.securebackup.impl.R
import io.element.android.features.securebackup.impl.tools.RecoveryKeyVisualTransformation
import io.element.android.libraries.designsystem.modifiers.autofill
import io.element.android.libraries.designsystem.modifiers.clickableIfNotNull
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -186,10 +187,9 @@ private fun RecoveryKeyFormContent(
modifier = Modifier
.fillMaxWidth()
.testTag(TestTags.recoveryKey)
.autofill(
autofillTypes = listOf(AutofillType.Password),
onFill = { onChange(it) },
),
.semantics {
contentType = ContentType.Password
},
minLines = 2,
value = state.formattedRecoveryKey.orEmpty(),
onValueChange = onChange,