Browse Source

Merge pull request #2376 from element-hq/feature/bma/testUi

More test on MessagesView, and harmonize preview on Dialogs
pull/2379/head
Benoit Marty 7 months ago committed by GitHub
parent
commit
c13b5566a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 43
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
  2. 1
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
  3. 15
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt
  4. 3
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt
  5. 11
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMenuStateProvider.kt
  6. 215
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt
  7. 10
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt
  8. 18
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt
  9. 13
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt
  10. 28
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt
  11. 27
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt
  12. 14
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt
  13. 23
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt
  14. 5
      libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt
  15. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ConfirmationDialogContent_null_Dialogs_ConfirmationDialogContent_0_null,NEXUS_5,1.0,en].png
  16. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ConfirmationDialog_null_ConfirmationDialog-Day_0_null,NEXUS_5,1.0,en].png
  17. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ConfirmationDialog_null_ConfirmationDialog-Night_1_null,NEXUS_5,1.0,en].png
  18. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ErrorDialogContent_null_Dialogs_ErrorDialogContent_0_null,NEXUS_5,1.0,en].png
  19. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ErrorDialog_null_ErrorDialog-Day_0_null,NEXUS_5,1.0,en].png
  20. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ErrorDialog_null_ErrorDialog-Night_1_null,NEXUS_5,1.0,en].png
  21. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ListDialogContent_null_Dialogs_ListDialogContent_0_null,NEXUS_5,1.0,en].png
  22. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ListDialogContent_null_ListDialogContent-Night_1_null,NEXUS_5,1.0,en].png
  23. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ListDialog_null_ListDialog-Day_0_null,NEXUS_5,1.0,en].png
  24. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ListDialog_null_ListDialog-Night_1_null,NEXUS_5,1.0,en].png
  25. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_MultipleSelectionDialogContent_null_Dialogs_MultipleSelectionDialogContent_0_null,NEXUS_5,1.0,en].png
  26. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_MultipleSelectionDialogContent_null_MultipleSelectionDialogContent-Night_1_null,NEXUS_5,1.0,en].png
  27. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_MultipleSelectionDialog_null_MultipleSelectionDialog-Day_0_null,NEXUS_5,1.0,en].png
  28. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_MultipleSelectionDialog_null_MultipleSelectionDialog-Night_1_null,NEXUS_5,1.0,en].png
  29. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_RetryDialogContent_null_Dialogs_RetryDialogContent_0_null,NEXUS_5,1.0,en].png
  30. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_RetryDialog_null_RetryDialog-Day_0_null,NEXUS_5,1.0,en].png
  31. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_RetryDialog_null_RetryDialog-Night_1_null,NEXUS_5,1.0,en].png
  32. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_SingleSelectionDialogContent_null_SingleSelectionDialogContent-Night_1_null,NEXUS_5,1.0,en].png
  33. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_SingleSelectionDialog_null_SingleSelectionDialog-Day_0_null,NEXUS_5,1.0,en].png
  34. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_SingleSelectionDialog_null_SingleSelectionDialog-Night_1_null,NEXUS_5,1.0,en].png
  35. 0
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_ProgressDialogContent_null_Dialogs_ProgressDialogContent_0_null,NEXUS_5,1.0,en].png
  36. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_ProgressDialog_null_ProgressDialog-Day_0_null,NEXUS_5,1.0,en].png
  37. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_ProgressDialog_null_ProgressDialog-Night_1_null,NEXUS_5,1.0,en].png

43
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt

@ -22,14 +22,18 @@ import io.element.android.features.messages.impl.actionlist.anActionListState @@ -22,14 +22,18 @@ import io.element.android.features.messages.impl.actionlist.anActionListState
import io.element.android.features.messages.impl.messagecomposer.AttachmentsState
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState
import io.element.android.features.messages.impl.timeline.TimelineState
import io.element.android.features.messages.impl.timeline.aTimelineItemList
import io.element.android.features.messages.impl.timeline.aTimelineState
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.aRetrySendMenuState
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.features.messages.impl.typing.aTypingNotificationState
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
@ -91,12 +95,20 @@ fun aMessagesState( @@ -91,12 +95,20 @@ fun aMessagesState(
roomName: AsyncData<String> = AsyncData.Success("Room name"),
roomAvatar: AsyncData<AvatarData> = AsyncData.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)),
userHasPermissionToSendMessage: Boolean = true,
userHasPermissionToRedactOwn: Boolean = false,
userHasPermissionToRedactOther: Boolean = false,
userHasPermissionToSendReaction: Boolean = true,
composerState: MessageComposerState = aMessageComposerState(
richTextEditorState = RichTextEditorState("Hello", initialFocus = true),
isFullScreen = false,
mode = MessageComposerMode.Normal,
),
voiceMessageComposerState: VoiceMessageComposerState = aVoiceMessageComposerState(),
timelineState: TimelineState = aTimelineState(
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
),
retrySendMenuState: RetrySendMenuState = aRetrySendMenuState(),
readReceiptBottomSheetState: ReadReceiptBottomSheetState = aReadReceiptBottomSheetState(),
actionListState: ActionListState = anActionListState(),
customReactionState: CustomReactionState = aCustomReactionState(),
reactionSummaryState: ReactionSummaryState = aReactionSummaryState(),
@ -110,23 +122,15 @@ fun aMessagesState( @@ -110,23 +122,15 @@ fun aMessagesState(
roomName = roomName,
roomAvatar = roomAvatar,
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
userHasPermissionToRedactOwn = false,
userHasPermissionToRedactOther = false,
userHasPermissionToSendReaction = true,
userHasPermissionToRedactOwn = userHasPermissionToRedactOwn,
userHasPermissionToRedactOther = userHasPermissionToRedactOther,
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
composerState = composerState,
voiceMessageComposerState = voiceMessageComposerState,
timelineState = aTimelineState(
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
),
typingNotificationState = aTypingNotificationState(),
retrySendMenuState = RetrySendMenuState(
selectedEvent = null,
eventSink = {},
),
readReceiptBottomSheetState = ReadReceiptBottomSheetState(
selectedEvent = null,
eventSink = {},
),
timelineState = timelineState,
retrySendMenuState = retrySendMenuState,
readReceiptBottomSheetState = readReceiptBottomSheetState,
actionListState = actionListState,
customReactionState = customReactionState,
reactionSummaryState = reactionSummaryState,
@ -150,9 +154,18 @@ fun aReactionSummaryState( @@ -150,9 +154,18 @@ fun aReactionSummaryState(
)
fun aCustomReactionState(
target: CustomReactionState.Target = CustomReactionState.Target.None,
eventSink: (CustomReactionEvents) -> Unit = {},
) = CustomReactionState(
target = CustomReactionState.Target.None,
target = target,
selectedEmoji = persistentSetOf(),
eventSink = eventSink,
)
fun aReadReceiptBottomSheetState(
selectedEvent: TimelineItem.Event? = null,
eventSink: (ReadReceiptBottomSheetEvents) -> Unit = {},
) = ReadReceiptBottomSheetState(
selectedEvent = selectedEvent,
eventSink = eventSink,
)

1
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt

@ -250,7 +250,6 @@ fun MessagesView( @@ -250,7 +250,6 @@ fun MessagesView(
state = state.customReactionState,
onEmojiSelected = { eventId, emoji ->
state.eventSink(MessagesEvents.ToggleReaction(emoji.unicode, eventId))
state.customReactionState.eventSink(CustomReactionEvents.DismissCustomReactionSheet)
}
)

15
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt

@ -48,12 +48,14 @@ import kotlin.random.Random @@ -48,12 +48,14 @@ import kotlin.random.Random
fun aTimelineState(
timelineItems: ImmutableList<TimelineItem> = persistentListOf(),
paginationState: MatrixTimeline.PaginationState = aPaginationState(),
renderReadReceipts: Boolean = false,
timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(),
eventSink: (TimelineEvents) -> Unit = {},
) = TimelineState(
timelineItems = timelineItems,
timelineRoomInfo = aTimelineRoomInfo(),
timelineRoomInfo = timelineRoomInfo,
paginationState = paginationState,
renderReadReceipts = false,
renderReadReceipts = renderReadReceipts,
highlightedEventId = null,
newEventState = NewEventState.None,
sessionState = aSessionState(
@ -196,9 +198,11 @@ internal fun aTimelineItemDebugInfo( @@ -196,9 +198,11 @@ internal fun aTimelineItemDebugInfo(
latestEditedJson
)
internal fun aTimelineItemReadReceipts(): TimelineItemReadReceipts {
internal fun aTimelineItemReadReceipts(
receipts: List<ReadReceiptData> = emptyList(),
): TimelineItemReadReceipts {
return TimelineItemReadReceipts(
receipts = emptyList<ReadReceiptData>().toImmutableList(),
receipts = receipts.toImmutableList(),
)
}
@ -232,8 +236,9 @@ internal fun aGroupedEvents( @@ -232,8 +236,9 @@ internal fun aGroupedEvents(
internal fun aTimelineRoomInfo(
isDirect: Boolean = false,
userHasPermissionToSendMessage: Boolean = true,
) = TimelineRoomInfo(
isDirect = isDirect,
userHasPermissionToSendMessage = true,
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
userHasPermissionToSendReaction = true,
)

3
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt

@ -51,6 +51,8 @@ import io.element.android.libraries.designsystem.theme.components.Icon @@ -51,6 +51,8 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonPlurals
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
@ -68,6 +70,7 @@ fun TimelineItemReadReceiptView( @@ -68,6 +70,7 @@ fun TimelineItemReadReceiptView(
ReadReceiptsAvatars(
receipts = state.receipts,
modifier = Modifier
.testTag(TestTags.messageReadReceipts)
.clip(RoundedCornerShape(4.dp))
.clickable {
onReadReceiptsClicked()

11
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMenuStateProvider.kt

@ -22,10 +22,15 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -22,10 +22,15 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
class RetrySendMenuStateProvider : PreviewParameterProvider<RetrySendMenuState> {
override val values: Sequence<RetrySendMenuState> = sequenceOf(
aRetrySendMenuState(event = null),
aRetrySendMenuState(),
aRetrySendMenuState(event = aTimelineItemEvent()),
)
}
fun aRetrySendMenuState(event: TimelineItem.Event? = aTimelineItemEvent()) =
RetrySendMenuState(selectedEvent = event, eventSink = {})
fun aRetrySendMenuState(
event: TimelineItem.Event? = null,
eventSink: (RetrySendMenuEvents) -> Unit = {},
) = RetrySendMenuState(
selectedEvent = event,
eventSink = eventSink,
)

215
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt

@ -27,18 +27,35 @@ import androidx.compose.ui.test.onAllNodesWithTag @@ -27,18 +27,35 @@ import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onFirst
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.performTouchInput
import androidx.compose.ui.test.swipeRight
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.emojibasebindings.Emoji
import io.element.android.emojibasebindings.EmojibaseCategory
import io.element.android.emojibasebindings.EmojibaseStore
import io.element.android.features.messages.impl.actionlist.ActionListEvents
import io.element.android.features.messages.impl.actionlist.ActionListState
import io.element.android.features.messages.impl.actionlist.anActionListState
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
import io.element.android.features.messages.impl.attachments.Attachment
import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
import io.element.android.features.messages.impl.timeline.aTimelineItemList
import io.element.android.features.messages.impl.timeline.aTimelineItemReadReceipts
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
import io.element.android.features.messages.impl.timeline.aTimelineState
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuEvents
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.aRetrySendMenuState
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.ui.strings.CommonStrings
@ -128,6 +145,136 @@ class MessagesViewTest { @@ -128,6 +145,136 @@ class MessagesViewTest {
callback.assertSuccess()
}
@Test
fun `clicking on an Event timestamp in error emits the expected Event`() {
val eventsRecorder = EventsRecorder<RetrySendMenuEvents>()
val state = aMessagesState(
retrySendMenuState = aRetrySendMenuState(
eventSink = eventsRecorder
),
)
val timelineItem = state.timelineState.timelineItems[1] as TimelineItem.Event
rule.setMessagesView(
state = state,
)
rule.onAllNodesWithText(timelineItem.sentTime)[1].performClick()
eventsRecorder.assertSingle(RetrySendMenuEvents.EventSelected(timelineItem))
}
@Test
fun `long clicking on an Event emits the expected Event userHasPermissionToSendMessage`() {
`long clicking on an Event emits the expected Event`(userHasPermissionToSendMessage = true)
}
@Test
fun `long clicking on an Event emits the expected Event userHasPermissionToRedactOwn`() {
`long clicking on an Event emits the expected Event`(userHasPermissionToRedactOwn = true)
}
@Test
fun `long clicking on an Event emits the expected Event userHasPermissionToRedactOther`() {
`long clicking on an Event emits the expected Event`(userHasPermissionToRedactOther = true)
}
@Test
fun `long clicking on an Event emits the expected Event userHasPermissionToSendReaction`() {
`long clicking on an Event emits the expected Event`(userHasPermissionToSendReaction = true)
}
private fun `long clicking on an Event emits the expected Event`(
userHasPermissionToSendMessage: Boolean = false,
userHasPermissionToRedactOwn: Boolean = false,
userHasPermissionToRedactOther: Boolean = false,
userHasPermissionToSendReaction: Boolean = false,
) {
val eventsRecorder = EventsRecorder<ActionListEvents>()
val state = aMessagesState(
actionListState = anActionListState(
eventSink = eventsRecorder
),
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
userHasPermissionToRedactOwn = userHasPermissionToRedactOwn,
userHasPermissionToRedactOther = userHasPermissionToRedactOther,
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
)
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event
rule.setMessagesView(
state = state,
)
// Cannot perform click on "Text", it's not detected. Use tag instead
rule.onAllNodesWithTag(TestTags.messageBubble.value).onFirst().performTouchInput { longClick() }
eventsRecorder.assertSingle(
ActionListEvents.ComputeForMessage(
event = timelineItem,
canRedactOwn = state.userHasPermissionToRedactOwn,
canRedactOther = state.userHasPermissionToRedactOther,
canSendMessage = state.userHasPermissionToSendMessage,
canSendReaction = state.userHasPermissionToSendReaction,
)
)
}
@Test
fun `clicking on a read receipt list emits the expected Event`() {
val eventsRecorder = EventsRecorder<ReadReceiptBottomSheetEvents>()
val state = aMessagesState(
timelineState = aTimelineState(
renderReadReceipts = true,
timelineItems = persistentListOf(
aTimelineItemEvent(
readReceiptState = aTimelineItemReadReceipts(
receipts = listOf(
aReadReceiptData(0),
),
),
),
),
),
readReceiptBottomSheetState = aReadReceiptBottomSheetState(
eventSink = eventsRecorder
),
)
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event
rule.setMessagesView(
state = state,
)
rule.onNodeWithTag(TestTags.messageReadReceipts.value).performClick()
eventsRecorder.assertSingle(ReadReceiptBottomSheetEvents.EventSelected(timelineItem))
}
@Test
fun `swiping on an Event emits the expected Event`() {
swipeTest(userHasPermissionToSendMessage = true)
}
@Test
fun `swiping on an Event emits no Event if user does not have permission to send message`() {
swipeTest(userHasPermissionToSendMessage = false)
}
private fun swipeTest(userHasPermissionToSendMessage: Boolean) {
val eventsRecorder = EventsRecorder<MessagesEvents>()
val state = aMessagesState(
timelineState = aTimelineState(
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
timelineRoomInfo = aTimelineRoomInfo(
userHasPermissionToSendMessage = userHasPermissionToSendMessage
),
),
eventSink = eventsRecorder,
)
rule.setMessagesView(
state = state,
)
rule.onAllNodesWithTag(TestTags.messageBubble.value).onFirst().performTouchInput { swipeRight(endX = 200f) }
if (userHasPermissionToSendMessage) {
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event
eventsRecorder.assertSingle(MessagesEvents.HandleAction(TimelineItemAction.Reply, timelineItem))
} else {
eventsRecorder.assertEmpty()
}
}
@Test
fun `clicking on send location invoke expected callback`() {
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
@ -256,6 +403,74 @@ class MessagesViewTest { @@ -256,6 +403,74 @@ class MessagesViewTest {
rule.onAllNodesWithContentDescription(moreReactionContentDescription).onFirst().performClick()
eventsRecorder.assertSingle(CustomReactionEvents.ShowCustomReactionSheet(timelineItem))
}
@Test
fun `clicking on more reaction from action list emits the expected Event`() {
val eventsRecorder = EventsRecorder<CustomReactionEvents>()
val state = aMessagesState()
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event
val stateWithActionListState = state.copy(
actionListState = anActionListState(
target = ActionListState.Target.Success(
event = timelineItem,
displayEmojiReactions = true,
actions = persistentListOf(TimelineItemAction.Edit),
),
),
customReactionState = aCustomReactionState(
eventSink = eventsRecorder
),
)
rule.setMessagesView(
state = stateWithActionListState,
)
val moreReactionContentDescription = rule.activity.getString(CommonStrings.a11y_react_with_other_emojis)
rule.onNodeWithContentDescription(moreReactionContentDescription).performClick()
// Give time for the close animation to complete
rule.mainClock.advanceTimeBy(milliseconds = 1_000)
eventsRecorder.assertSingle(CustomReactionEvents.ShowCustomReactionSheet(timelineItem))
}
@Test
fun `clicking on a custom emoji emits the expected Events`() {
val aUnicode = "🙈"
val customReactionStateEventsRecorder = EventsRecorder<CustomReactionEvents>()
val eventsRecorder = EventsRecorder<MessagesEvents>()
val state = aMessagesState(
eventSink = eventsRecorder,
)
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event
val stateWithCustomReactionState = state.copy(
customReactionState = aCustomReactionState(
target = CustomReactionState.Target.Success(
event = timelineItem,
emojibaseStore = EmojibaseStore(
categories = mapOf(
EmojibaseCategory.People to listOf(
Emoji(
hexcode = "",
label = "",
tags = emptyList(),
shortcodes = emptyList(),
unicode = aUnicode,
skins = null,
)
)
)
),
),
eventSink = customReactionStateEventsRecorder
),
)
rule.setMessagesView(
state = stateWithCustomReactionState,
)
rule.onNodeWithText(aUnicode, useUnmergedTree = true).performClick()
// Give time for the close animation to complete
rule.mainClock.advanceTimeBy(milliseconds = 1_000)
customReactionStateEventsRecorder.assertSingle(CustomReactionEvents.DismissCustomReactionSheet)
eventsRecorder.assertSingle(MessagesEvents.ToggleReaction(aUnicode, timelineItem.eventId!!))
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMessagesView(

10
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt

@ -35,8 +35,10 @@ import androidx.compose.ui.tooling.preview.Preview @@ -35,8 +35,10 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.DialogPreview
import io.element.android.libraries.designsystem.theme.components.Text
@ -141,8 +143,14 @@ private fun ProgressDialogContent( @@ -141,8 +143,14 @@ private fun ProgressDialogContent(
@Preview(group = PreviewGroup.Dialogs)
@Composable
internal fun ProgressDialogPreview() = ElementThemedPreview {
internal fun ProgressDialogContentPreview() = ElementThemedPreview {
DialogPreview {
ProgressDialogContent(text = "test dialog content", isCancellable = true)
}
}
@PreviewsDayNight
@Composable
internal fun ProgressDialogPreview() = ElementPreview {
ProgressDialog(text = "test dialog content", isCancellable = true)
}

18
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt

@ -22,8 +22,10 @@ import androidx.compose.runtime.Composable @@ -22,8 +22,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.DialogPreview
import io.element.android.libraries.designsystem.theme.components.SimpleAlertDialogContent
import io.element.android.libraries.ui.strings.CommonStrings
@ -87,7 +89,7 @@ private fun ConfirmationDialogContent( @@ -87,7 +89,7 @@ private fun ConfirmationDialogContent(
@Preview(group = PreviewGroup.Dialogs)
@Composable
internal fun ConfirmationDialogPreview() =
internal fun ConfirmationDialogContentPreview() =
ElementThemedPreview(showBackground = false) {
DialogPreview {
ConfirmationDialogContent(
@ -102,3 +104,17 @@ internal fun ConfirmationDialogPreview() = @@ -102,3 +104,17 @@ internal fun ConfirmationDialogPreview() =
)
}
}
@PreviewsDayNight
@Composable
internal fun ConfirmationDialogPreview() = ElementPreview {
ConfirmationDialog(
content = "Content",
title = "Title",
submitText = "OK",
cancelText = "Cancel",
thirdButtonText = "Disable",
onSubmitClicked = {},
onDismiss = {}
)
}

13
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt

@ -22,8 +22,10 @@ import androidx.compose.runtime.Composable @@ -22,8 +22,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.DialogPreview
import io.element.android.libraries.designsystem.theme.components.SimpleAlertDialogContent
import io.element.android.libraries.ui.strings.CommonStrings
@ -69,7 +71,7 @@ object ErrorDialogDefaults { @@ -69,7 +71,7 @@ object ErrorDialogDefaults {
@Preview(group = PreviewGroup.Dialogs)
@Composable
internal fun ErrorDialogPreview() {
internal fun ErrorDialogContentPreview() {
ElementThemedPreview(showBackground = false) {
DialogPreview {
ErrorDialogContent(
@ -79,3 +81,12 @@ internal fun ErrorDialogPreview() { @@ -79,3 +81,12 @@ internal fun ErrorDialogPreview() {
}
}
}
@PreviewsDayNight
@Composable
internal fun ErrorDialogPreview() = ElementPreview {
ErrorDialog(
content = "Content",
onDismiss = {},
)
}

28
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt

@ -24,10 +24,11 @@ import androidx.compose.material3.ExperimentalMaterial3Api @@ -24,10 +24,11 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import io.element.android.libraries.designsystem.components.list.TextFieldListItem
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.DialogPreview
@ -100,11 +101,10 @@ private fun ListDialogContent( @@ -100,11 +101,10 @@ private fun ListDialogContent(
}
}
@PreviewsDayNight
@ShowkaseComposable(group = PreviewGroup.Dialogs)
@Preview(group = PreviewGroup.Dialogs)
@Composable
internal fun ListDialogContentPreview() {
ElementPreview(showBackground = false) {
ElementThemedPreview(showBackground = false) {
DialogPreview {
ListDialogContent(
listItems = {
@ -124,3 +124,23 @@ internal fun ListDialogContentPreview() { @@ -124,3 +124,23 @@ internal fun ListDialogContentPreview() {
}
}
}
@PreviewsDayNight
@Composable
internal fun ListDialogPreview() = ElementPreview {
ListDialog(
listItems = {
item {
TextFieldListItem(placeholder = "Text input", text = "", onTextChanged = {})
}
item {
TextFieldListItem(placeholder = "Another text input", text = "", onTextChanged = {})
}
},
title = "Dialog title",
onDismissRequest = {},
onSubmit = {},
cancelText = "Cancel",
submitText = "Save",
)
}

27
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt

@ -26,10 +26,11 @@ import androidx.compose.runtime.remember @@ -26,10 +26,11 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import io.element.android.libraries.designsystem.components.list.CheckboxListItem
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.DialogPreview
@ -124,11 +125,10 @@ private fun MultipleSelectionDialogContent( @@ -124,11 +125,10 @@ private fun MultipleSelectionDialogContent(
}
}
@PreviewsDayNight
@ShowkaseComposable(group = PreviewGroup.Dialogs)
@Preview(group = PreviewGroup.Dialogs)
@Composable
internal fun MultipleSelectionDialogContentPreview() {
ElementPreview(showBackground = false) {
ElementThemedPreview(showBackground = false) {
DialogPreview {
val options = persistentListOf(
ListOption("Option 1", "Supporting line text lorem ipsum dolor sit amet, consectetur."),
@ -147,3 +147,22 @@ internal fun MultipleSelectionDialogContentPreview() { @@ -147,3 +147,22 @@ internal fun MultipleSelectionDialogContentPreview() {
}
}
}
@PreviewsDayNight
@Composable
internal fun MultipleSelectionDialogPreview() = ElementPreview {
val options = persistentListOf(
ListOption("Option 1", "Supporting line text lorem ipsum dolor sit amet, consectetur."),
ListOption("Option 2"),
ListOption("Option 3"),
)
MultipleSelectionDialog(
title = "Dialog title",
options = options,
onConfirmClicked = {},
onDismissRequest = {},
confirmButtonTitle = "Save",
dismissButtonTitle = "Cancel",
initialSelection = persistentListOf(0),
)
}

14
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt

@ -22,8 +22,10 @@ import androidx.compose.runtime.Composable @@ -22,8 +22,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.DialogPreview
import io.element.android.libraries.designsystem.theme.components.SimpleAlertDialogContent
import io.element.android.libraries.ui.strings.CommonStrings
@ -78,7 +80,7 @@ object RetryDialogDefaults { @@ -78,7 +80,7 @@ object RetryDialogDefaults {
@Preview(group = PreviewGroup.Dialogs)
@Composable
internal fun RetryDialogPreview() {
internal fun RetryDialogContentPreview() {
ElementThemedPreview(showBackground = false) {
DialogPreview {
RetryDialogContent(
@ -89,3 +91,13 @@ internal fun RetryDialogPreview() { @@ -89,3 +91,13 @@ internal fun RetryDialogPreview() {
}
}
}
@PreviewsDayNight
@Composable
internal fun RetryDialogPreview() = ElementPreview {
RetryDialog(
content = "Content",
onRetry = {},
onDismiss = {},
)
}

23
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt

@ -24,8 +24,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api @@ -24,8 +24,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import io.element.android.libraries.designsystem.components.list.RadioButtonListItem
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
@ -105,8 +105,7 @@ private fun SingleSelectionDialogContent( @@ -105,8 +105,7 @@ private fun SingleSelectionDialogContent(
}
}
@PreviewsDayNight
@ShowkaseComposable(group = PreviewGroup.Dialogs)
@Preview(group = PreviewGroup.Dialogs)
@Composable
internal fun SingleSelectionDialogContentPreview() {
ElementPreview(showBackground = false) {
@ -127,3 +126,21 @@ internal fun SingleSelectionDialogContentPreview() { @@ -127,3 +126,21 @@ internal fun SingleSelectionDialogContentPreview() {
}
}
}
@PreviewsDayNight
@Composable
internal fun SingleSelectionDialogPreview() = ElementPreview {
val options = persistentListOf(
ListOption("Option 1"),
ListOption("Option 2"),
ListOption("Option 3"),
)
SingleSelectionDialog(
title = "Dialog title",
options = options,
onOptionSelected = {},
onDismissRequest = {},
dismissButtonTitle = "Cancel",
initialSelection = 0
)
}

5
libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt

@ -63,6 +63,11 @@ object TestTags { @@ -63,6 +63,11 @@ object TestTags {
*/
val messageBubble = TestTag("message_bubble")
/**
* Message Read Receipts.
*/
val messageReadReceipts = TestTag("message_read_receipts")
/**
* Dialogs.
*/

0
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ConfirmationDialog_null_Dialogs_ConfirmationDialog_0_null,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ConfirmationDialogContent_null_Dialogs_ConfirmationDialogContent_0_null,NEXUS_5,1.0,en].png

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ConfirmationDialog_null_ConfirmationDialog-Day_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ConfirmationDialog_null_ConfirmationDialog-Night_1_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

0
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ErrorDialog_null_Dialogs_ErrorDialog_0_null,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ErrorDialogContent_null_Dialogs_ErrorDialogContent_0_null,NEXUS_5,1.0,en].png

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ErrorDialog_null_ErrorDialog-Day_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ErrorDialog_null_ErrorDialog-Night_1_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ListDialogContent_null_Dialogs_ListDialogContent_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ListDialogContent_null_ListDialogContent-Night_1_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ListDialog_null_ListDialog-Day_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_ListDialog_null_ListDialog-Night_1_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_MultipleSelectionDialogContent_null_Dialogs_MultipleSelectionDialogContent_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_MultipleSelectionDialogContent_null_MultipleSelectionDialogContent-Night_1_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_MultipleSelectionDialog_null_MultipleSelectionDialog-Day_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_MultipleSelectionDialog_null_MultipleSelectionDialog-Night_1_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

0
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_RetryDialog_null_Dialogs_RetryDialog_0_null,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_RetryDialogContent_null_Dialogs_RetryDialogContent_0_null,NEXUS_5,1.0,en].png

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_RetryDialog_null_RetryDialog-Day_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_RetryDialog_null_RetryDialog-Night_1_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_SingleSelectionDialogContent_null_SingleSelectionDialogContent-Night_1_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_SingleSelectionDialog_null_SingleSelectionDialog-Day_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.dialogs_SingleSelectionDialog_null_SingleSelectionDialog-Night_1_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

0
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_ProgressDialog_null_Dialogs_ProgressDialog_0_null,NEXUS_5,1.0,en].png → tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_ProgressDialogContent_null_Dialogs_ProgressDialogContent_0_null,NEXUS_5,1.0,en].png

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_ProgressDialog_null_ProgressDialog-Day_0_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components_ProgressDialog_null_ProgressDialog-Night_1_null,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.
Loading…
Cancel
Save