Merge pull request #6693 from element-hq/renovate/compose.bom

Update dependency androidx.compose:compose-bom to v2026.04.01
This commit is contained in:
Benoit Marty 2026-04-30 16:54:23 +02:00 committed by GitHub
commit 4e38846342
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
84 changed files with 2198 additions and 2321 deletions

View file

@ -6,12 +6,15 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.libraries.mediaviewer.impl.details
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.AndroidComposeUiTest
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.ui.strings.CommonStrings
@ -21,43 +24,38 @@ 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 org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class MediaDeleteConfirmationBottomSheetTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on Cancel invokes expected callback`() {
fun `clicking on Cancel invokes expected callback`() = runAndroidComposeUiTest {
val state = aMediaBottomSheetStateDeleteConfirmation()
ensureCalledOnce { callback ->
rule.setMediaDeleteConfirmationBottomSheet(
setMediaDeleteConfirmationBottomSheet(
state = state,
onDismiss = callback,
)
rule.clickOn(CommonStrings.action_cancel)
clickOn(CommonStrings.action_cancel)
}
}
@Test
fun `clicking on Remove invokes expected callback`() {
fun `clicking on Remove invokes expected callback`() = runAndroidComposeUiTest {
val state = aMediaBottomSheetStateDeleteConfirmation()
ensureCalledOnceWithParam(state.eventId) { callback ->
rule.setMediaDeleteConfirmationBottomSheet(
setMediaDeleteConfirmationBottomSheet(
state = state,
onDelete = callback,
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.action_remove)).assertExists()
rule.clickOn(CommonStrings.action_remove)
onNodeWithText(activity!!.getString(CommonStrings.action_remove)).assertExists()
clickOn(CommonStrings.action_remove)
}
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMediaDeleteConfirmationBottomSheet(
private fun AndroidComposeUiTest<ComponentActivity>.setMediaDeleteConfirmationBottomSheet(
state: MediaBottomSheetState.DeleteConfirmation,
onDelete: (EventId) -> Unit = EnsureNeverCalledWithParam(),
onDismiss: () -> Unit = EnsureNeverCalled(),

View file

@ -6,12 +6,15 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.libraries.mediaviewer.impl.details
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.AndroidComposeUiTest
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.ui.strings.CommonStrings
@ -20,97 +23,92 @@ import io.element.android.tests.testutils.EnsureNeverCalledWithParam
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnceWithParam
import io.element.android.tests.testutils.setSafeContent
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 MediaDetailsBottomSheetTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
@Config(qualifiers = "h1024dp")
fun `clicking on View in timeline invokes expected callback`() {
fun `clicking on View in timeline invokes expected callback`() = runAndroidComposeUiTest {
val state = aMediaBottomSheetStateDetails()
ensureCalledOnceWithParam(state.eventId) { callback ->
rule.setMediaDetailsBottomSheet(
setMediaDetailsBottomSheet(
state = state,
onViewInTimeline = callback,
)
rule.clickOn(CommonStrings.action_view_in_timeline)
clickOn(CommonStrings.action_view_in_timeline)
}
}
@Test
@Config(qualifiers = "h1024dp")
fun `clicking on Share invokes expected callback`() {
fun `clicking on Share invokes expected callback`() = runAndroidComposeUiTest {
val state = aMediaBottomSheetStateDetails()
ensureCalledOnceWithParam(state.eventId) { callback ->
rule.setMediaDetailsBottomSheet(
setMediaDetailsBottomSheet(
state = state,
onShare = callback,
)
rule.clickOn(CommonStrings.action_share)
clickOn(CommonStrings.action_share)
}
}
@Test
@Config(qualifiers = "h1024dp")
fun `clicking on Forward invokes expected callback`() {
fun `clicking on Forward invokes expected callback`() = runAndroidComposeUiTest {
val state = aMediaBottomSheetStateDetails()
ensureCalledOnceWithParam(state.eventId) { callback ->
rule.setMediaDetailsBottomSheet(
setMediaDetailsBottomSheet(
state = state,
onForward = callback,
)
rule.clickOn(CommonStrings.action_forward)
clickOn(CommonStrings.action_forward)
}
}
@Test
@Config(qualifiers = "h1024dp")
fun `clicking on Download invokes expected callback`() {
fun `clicking on Download invokes expected callback`() = runAndroidComposeUiTest {
val state = aMediaBottomSheetStateDetails()
ensureCalledOnceWithParam(state.eventId) { callback ->
rule.setMediaDetailsBottomSheet(
setMediaDetailsBottomSheet(
state = state,
onDownload = callback,
)
rule.clickOn(CommonStrings.action_download)
clickOn(CommonStrings.action_download)
}
}
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on Delete invokes expected callback`() {
fun `clicking on Delete invokes expected callback`() = runAndroidComposeUiTest {
val state = aMediaBottomSheetStateDetails()
ensureCalledOnceWithParam(state.eventId) { callback ->
rule.setMediaDetailsBottomSheet(
setMediaDetailsBottomSheet(
state = state,
onDelete = callback,
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.action_delete)).assertExists()
rule.clickOn(CommonStrings.action_delete)
onNodeWithText(activity!!.getString(CommonStrings.action_delete)).assertExists()
clickOn(CommonStrings.action_delete)
}
}
@Config(qualifiers = "h1024dp")
@Test
fun `Remove is not present if canDelete is false`() {
fun `Remove is not present if canDelete is false`() = runAndroidComposeUiTest {
val state = aMediaBottomSheetStateDetails(
canDelete = false,
)
rule.setMediaDetailsBottomSheet(
setMediaDetailsBottomSheet(
state = state,
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.action_remove)).assertDoesNotExist()
onNodeWithText(activity!!.getString(CommonStrings.action_remove)).assertDoesNotExist()
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMediaDetailsBottomSheet(
private fun AndroidComposeUiTest<ComponentActivity>.setMediaDetailsBottomSheet(
state: MediaBottomSheetState.Details,
onViewInTimeline: (EventId) -> Unit = EnsureNeverCalledWithParam(),
onShare: (EventId) -> Unit = EnsureNeverCalledWithParam(),

View file

@ -6,18 +6,21 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.libraries.mediaviewer.impl.viewer
import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.annotation.StringRes
import androidx.compose.ui.test.AndroidComposeUiTest
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assertHasClickAction
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.performClick
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.mediaviewer.impl.details.aMediaBottomSheetStateDetails
@ -30,30 +33,26 @@ import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.pressBack
import io.element.android.tests.testutils.setSafeContent
import io.mockk.mockk
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 MediaViewerViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
private val mockMediaUrl: Uri = mockk("localMediaUri")
@Test
fun `clicking on back invokes expected callback`() {
fun `clicking on back invokes expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<MediaViewerEvent>()
val state = aMediaViewerState(
eventSink = eventsRecorder
)
ensureCalledOnce { callback ->
rule.setMediaViewerView(
setMediaViewerView(
state = state,
onBackClick = callback,
)
rule.pressBack()
pressBack()
}
eventsRecorder.assertList(
listOf(
@ -103,16 +102,16 @@ class MediaViewerViewTest {
data: MediaViewerPageData.MediaViewerData,
@StringRes contentDescriptionRes: Int,
expectedEvent: MediaViewerEvent,
) {
) = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<MediaViewerEvent>()
rule.setMediaViewerView(
setMediaViewerView(
aMediaViewerState(
listData = listOf(data),
eventSink = eventsRecorder
),
)
val contentDescription = rule.activity.getString(contentDescriptionRes)
rule.onNodeWithContentDescription(contentDescription).performClick()
val contentDescription = activity!!.getString(contentDescriptionRes)
onNodeWithContentDescription(contentDescription).performClick()
eventsRecorder.assertList(
listOf(
MediaViewerEvent.OnNavigateTo(0),
@ -159,16 +158,16 @@ class MediaViewerViewTest {
data: MediaViewerPageData.MediaViewerData,
@StringRes textRes: Int,
expectedEvent: MediaViewerEvent,
) {
) = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<MediaViewerEvent>()
rule.setMediaViewerView(
setMediaViewerView(
aMediaViewerState(
listData = listOf(data),
mediaBottomSheetState = aMediaBottomSheetStateDetails(),
eventSink = eventsRecorder
),
)
rule.clickOn(textRes)
clickOn(textRes)
eventsRecorder.assertList(
listOf(
MediaViewerEvent.OnNavigateTo(0),
@ -179,24 +178,25 @@ class MediaViewerViewTest {
}
@Test
fun `clicking on image hides the overlay`() {
fun `clicking on image hides the overlay`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<MediaViewerEvent>()
val state = aMediaViewerState(
eventSink = eventsRecorder
)
rule.setMediaViewerView(
setMediaViewerView(
state = state,
)
// Ensure that the action are visible
val contentDescription = rule.activity.getString(CommonStrings.action_share)
rule.onNodeWithContentDescription(contentDescription)
val resources = activity!!.resources
val contentDescription = resources.getString(CommonStrings.action_share)
onNodeWithContentDescription(contentDescription)
.assertExists()
.assertHasClickAction()
val imageContentDescription = rule.activity.getString(CommonStrings.common_image)
rule.onNodeWithContentDescription(imageContentDescription).performClick()
val imageContentDescription = resources.getString(CommonStrings.common_image)
onNodeWithContentDescription(imageContentDescription).performClick()
// Give time for the animation (? since even by removing AnimatedVisibility it still fails)
rule.mainClock.advanceTimeBy(1_000)
rule.onNodeWithContentDescription(contentDescription)
mainClock.advanceTimeBy(1_000)
onNodeWithContentDescription(contentDescription)
.assertDoesNotExist()
eventsRecorder.assertList(
listOf(
@ -207,19 +207,19 @@ class MediaViewerViewTest {
}
@Test
fun `clicking swipe on the image invokes the expected callback`() {
fun `clicking swipe on the image invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<MediaViewerEvent>()
val state = aMediaViewerState(
eventSink = eventsRecorder
)
ensureCalledOnce { callback ->
rule.setMediaViewerView(
setMediaViewerView(
state = state,
onBackClick = callback,
)
val imageContentDescription = rule.activity.getString(CommonStrings.common_image)
rule.onNodeWithContentDescription(imageContentDescription).performTouchInput { swipeDown(startY = centerY) }
rule.mainClock.advanceTimeBy(1_000)
val imageContentDescription = activity!!.getString(CommonStrings.common_image)
onNodeWithContentDescription(imageContentDescription).performTouchInput { swipeDown(startY = centerY) }
mainClock.advanceTimeBy(1_000)
}
eventsRecorder.assertList(
listOf(
@ -230,18 +230,18 @@ class MediaViewerViewTest {
}
@Test
fun `error case, click on retry emits the expected Event`() {
fun `error case, click on retry emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<MediaViewerEvent>()
val data = aMediaViewerPageData(
downloadedMedia = AsyncData.Failure(IllegalStateException("error")),
)
rule.setMediaViewerView(
setMediaViewerView(
aMediaViewerState(
listData = listOf(data),
eventSink = eventsRecorder
),
)
rule.clickOn(CommonStrings.action_retry)
clickOn(CommonStrings.action_retry)
eventsRecorder.assertList(
listOf(
MediaViewerEvent.OnNavigateTo(0),
@ -252,18 +252,18 @@ class MediaViewerViewTest {
}
@Test
fun `error case, click on cancel emits the expected Event`() {
fun `error case, click on cancel emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<MediaViewerEvent>()
val data = aMediaViewerPageData(
downloadedMedia = AsyncData.Failure(IllegalStateException("error")),
)
rule.setMediaViewerView(
setMediaViewerView(
aMediaViewerState(
listData = listOf(data),
eventSink = eventsRecorder
),
)
rule.clickOn(CommonStrings.action_cancel)
clickOn(CommonStrings.action_cancel)
eventsRecorder.assertList(
listOf(
MediaViewerEvent.OnNavigateTo(0),
@ -274,7 +274,7 @@ class MediaViewerViewTest {
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMediaViewerView(
private fun AndroidComposeUiTest<ComponentActivity>.setMediaViewerView(
state: MediaViewerState,
onBackClick: () -> Unit = EnsureNeverCalled(),
) {

View file

@ -6,12 +6,15 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.libraries.textcomposer.impl.components.markdown
import android.widget.EditText
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.AndroidComposeUiTest
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import androidx.core.text.getSpans
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
@ -32,66 +35,54 @@ import io.element.android.libraries.textcomposer.model.SuggestionType
import io.element.android.libraries.textcomposer.model.aMarkdownTextEditorState
import io.element.android.tests.testutils.EnsureCalledOnceWithParam
import io.element.android.tests.testutils.EventsRecorder
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class MarkdownTextInputTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `when user types onTyping is triggered with value 'true'`() = runTest {
fun `when user types onTyping is triggered with value 'true'`() = runAndroidComposeUiTest {
val state = aMarkdownTextEditorState(initialFocus = true)
val onTyping = EnsureCalledOnceWithParam(expectedParam = true, result = Unit)
rule.setMarkdownTextInput(state = state, onTyping = onTyping)
rule.activityRule.scenario.onActivity {
it.findEditor().setText("Test")
}
rule.awaitIdle()
setMarkdownTextInput(state = state, onTyping = onTyping)
activity!!.findEditor().setText("Test")
awaitIdle()
onTyping.assertSuccess()
}
@Test
fun `when user removes text onTyping is triggered with value 'false'`() = runTest {
fun `when user removes text onTyping is triggered with value 'false'`() = runAndroidComposeUiTest {
val state = aMarkdownTextEditorState(initialFocus = true)
val onTyping = EventsRecorder<Boolean>()
rule.setMarkdownTextInput(state = state, onTyping = onTyping)
rule.activityRule.scenario.onActivity {
val editText = it.findEditor()
editText.setText("Test")
editText.setText("")
editText.setText(null)
}
rule.awaitIdle()
setMarkdownTextInput(state = state, onTyping = onTyping)
val editText = activity!!.findEditor()
editText.setText("Test")
editText.setText("")
editText.setText(null)
awaitIdle()
onTyping.assertList(listOf(true, false, false))
}
@Test
fun `when user types something that's not a mention onSuggestionReceived is triggered with 'null'`() = runTest {
fun `when user types something that's not a mention onSuggestionReceived is triggered with 'null'`() = runAndroidComposeUiTest {
val state = aMarkdownTextEditorState(initialFocus = true)
val onSuggestionReceived = EventsRecorder<Suggestion?>()
rule.setMarkdownTextInput(state = state, onSuggestionReceived = onSuggestionReceived)
rule.activityRule.scenario.onActivity {
it.findEditor().setText("Test")
}
rule.awaitIdle()
setMarkdownTextInput(state = state, onSuggestionReceived = onSuggestionReceived)
activity!!.findEditor().setText("Test")
awaitIdle()
onSuggestionReceived.assertSingle(null)
}
@Test
fun `when user types something that's a mention onSuggestionReceived is triggered a real value`() = runTest {
fun `when user types something that's a mention onSuggestionReceived is triggered a real value`() = runAndroidComposeUiTest {
val state = aMarkdownTextEditorState(initialFocus = true)
val onSuggestionReceived = EventsRecorder<Suggestion?>()
rule.setMarkdownTextInput(state = state, onSuggestionReceived = onSuggestionReceived)
rule.activityRule.scenario.onActivity {
it.findEditor().setText("@")
it.findEditor().setText("#")
it.findEditor().setText("/")
}
rule.awaitIdle()
setMarkdownTextInput(state = state, onSuggestionReceived = onSuggestionReceived)
val editor = activity!!.findEditor()
editor.setText("@")
editor.setText("#")
editor.setText("/")
awaitIdle()
onSuggestionReceived.assertList(
listOf(
// User mention suggestion
@ -105,69 +96,59 @@ class MarkdownTextInputTest {
}
@Test
fun `when the selection changes in the UI the state is updated`() = runTest {
fun `when the selection changes in the UI the state is updated`() = runAndroidComposeUiTest {
val state = aMarkdownTextEditorState(initialText = "Test", initialFocus = true)
rule.setMarkdownTextInput(state = state)
rule.activityRule.scenario.onActivity {
val editor = it.findEditor()
editor.setSelection(2)
}
rule.awaitIdle()
setMarkdownTextInput(state = state)
val editor = activity!!.findEditor()
editor.setSelection(2)
awaitIdle()
// Selection is updated
assertThat(state.selection).isEqualTo(2..2)
}
@Test
fun `when the selection state changes in the view is updated`() = runTest {
fun `when the selection state changes in the view is updated`() = runAndroidComposeUiTest {
val state = aMarkdownTextEditorState(initialText = "Test", initialFocus = true)
rule.setMarkdownTextInput(state = state)
var editor: EditText? = null
rule.activityRule.scenario.onActivity {
editor = it.findEditor()
state.selection = 2..2
}
rule.awaitIdle()
setMarkdownTextInput(state = state)
val editor = activity!!.findEditor()
state.selection = 2..2
awaitIdle()
// Selection state is updated
assertThat(editor?.selectionStart).isEqualTo(2)
assertThat(editor?.selectionEnd).isEqualTo(2)
assertThat(editor.selectionStart).isEqualTo(2)
assertThat(editor.selectionEnd).isEqualTo(2)
}
@Test
fun `when the view focus changes the state is updated`() = runTest {
fun `when the view focus changes the state is updated`() = runAndroidComposeUiTest {
val state = aMarkdownTextEditorState(initialText = "Test", initialFocus = false)
rule.setMarkdownTextInput(state = state)
rule.activityRule.scenario.onActivity {
val editor = it.findEditor()
editor.requestFocus()
}
setMarkdownTextInput(state = state)
val editor = activity!!.findEditor()
editor.requestFocus()
// Focus state is updated
assertThat(state.hasFocus).isTrue()
}
@Test
fun `inserting a mention replaces the existing text with a span`() = runTest {
fun `inserting a mention replaces the existing text with a span`() = runAndroidComposeUiTest {
val permalinkParser = FakePermalinkParser(result = { PermalinkData.UserLink(A_SESSION_ID) })
val state = aMarkdownTextEditorState(initialText = "@", initialFocus = true)
state.currentSuggestion = Suggestion(0, 1, SuggestionType.Mention, "")
rule.setMarkdownTextInput(state = state)
var editor: EditText? = null
rule.activityRule.scenario.onActivity {
editor = it.findEditor()
state.insertSuggestion(
ResolvedSuggestion.Member(roomMember = aRoomMember()),
aMentionSpanProvider(permalinkParser),
)
}
rule.awaitIdle()
setMarkdownTextInput(state = state)
val editor = activity!!.findEditor()
state.insertSuggestion(
ResolvedSuggestion.Member(roomMember = aRoomMember()),
aMentionSpanProvider(permalinkParser),
)
awaitIdle()
// Text is replaced with a placeholder
assertThat(editor?.editableText.toString()).isEqualTo("@ ")
assertThat(editor.editableText.toString()).isEqualTo("@ ")
// The placeholder contains a MentionSpan
val mentionSpans = editor?.editableText?.getSpans<MentionSpan>(0, 2).orEmpty()
val mentionSpans = editor.editableText?.getSpans<MentionSpan>(0, 2).orEmpty()
assertThat(mentionSpans).isNotEmpty()
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMarkdownTextInput(
private fun AndroidComposeUiTest<ComponentActivity>.setMarkdownTextInput(
state: MarkdownTextEditorState = aMarkdownTextEditorState(),
onTyping: (Boolean) -> Unit = {},
onSuggestionReceived: (Suggestion?) -> Unit = {},

View file

@ -6,60 +6,58 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.libraries.troubleshoot.impl
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.AndroidComposeUiTest
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.tests.testutils.EnsureNeverCalled
import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.ensureCalledOnce
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 TroubleshootNotificationsViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `press menu back invokes the expected callback`() {
fun `press menu back invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<TroubleshootNotificationsEvents>(expectEvents = false)
ensureCalledOnce {
rule.setTroubleshootNotificationsView(
setTroubleshootNotificationsView(
state = aTroubleshootNotificationsState(
eventSink = eventsRecorder
),
onBackClick = it,
)
rule.pressBack()
pressBack()
}
}
@Test
fun `clicking on run test emits the expected Event`() {
fun `clicking on run test emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<TroubleshootNotificationsEvents>()
rule.setTroubleshootNotificationsView(
setTroubleshootNotificationsView(
aTroubleshootNotificationsState(
eventSink = eventsRecorder
),
)
rule.onNodeWithText("Run tests").performClick()
onNodeWithText("Run tests").performClick()
eventsRecorder.assertSingle(TroubleshootNotificationsEvents.StartTests)
}
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on run test again emits the expected Event`() {
fun `clicking on run test again emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<TroubleshootNotificationsEvents>()
rule.setTroubleshootNotificationsView(
setTroubleshootNotificationsView(
aTroubleshootNotificationsState(
tests = listOf(
aTroubleshootTestStateFailure(
@ -69,7 +67,7 @@ class TroubleshootNotificationsViewTest {
eventSink = eventsRecorder
),
)
rule.onNodeWithText("Run tests again").performClick()
onNodeWithText("Run tests again").performClick()
eventsRecorder.assertList(
listOf(
TroubleshootNotificationsEvents.RetryFailedTests,
@ -80,9 +78,9 @@ class TroubleshootNotificationsViewTest {
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on quick fix emits the expected Event`() {
fun `clicking on quick fix emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<TroubleshootNotificationsEvents>()
rule.setTroubleshootNotificationsView(
setTroubleshootNotificationsView(
aTroubleshootNotificationsState(
tests = listOf(
aTroubleshootTestStateFailure(
@ -92,7 +90,7 @@ class TroubleshootNotificationsViewTest {
eventSink = eventsRecorder
),
)
rule.onNodeWithText("Attempt to fix").performClick()
onNodeWithText("Attempt to fix").performClick()
eventsRecorder.assertList(
listOf(
TroubleshootNotificationsEvents.RetryFailedTests,
@ -102,7 +100,7 @@ class TroubleshootNotificationsViewTest {
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTroubleshootNotificationsView(
private fun AndroidComposeUiTest<ComponentActivity>.setTroubleshootNotificationsView(
state: TroubleshootNotificationsState,
onBackClick: () -> Unit = EnsureNeverCalled(),
) {

View file

@ -6,14 +6,17 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.libraries.troubleshoot.impl.history
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.AndroidComposeUiTest
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_FORMATTED_DATE
@ -23,67 +26,62 @@ import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EnsureNeverCalled
import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PushHistoryViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on Reset sends a PushHistoryEvents`() {
fun `clicking on Reset sends a PushHistoryEvents`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PushHistoryEvents>()
rule.setPushHistoryView(
setPushHistoryView(
aPushHistoryState(
pushCounter = 123,
eventSink = eventsRecorder,
),
)
val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu)
rule.onNodeWithContentDescription(menuContentDescription).performClick()
rule.clickOn(CommonStrings.action_reset)
val menuContentDescription = activity!!.getString(CommonStrings.a11y_user_menu)
onNodeWithContentDescription(menuContentDescription).performClick()
clickOn(CommonStrings.action_reset)
eventsRecorder.assertSingle(PushHistoryEvents.Reset(requiresConfirmation = true))
// Also check that the push counter is rendered
rule.onNodeWithText("123").assertExists()
onNodeWithText("123").assertExists()
}
@Test
fun `clicking on show only errors sends a PushHistoryEvents(true)`() {
fun `clicking on show only errors sends a PushHistoryEvents(true)`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PushHistoryEvents>()
rule.setPushHistoryView(
setPushHistoryView(
aPushHistoryState(
showOnlyErrors = false,
eventSink = eventsRecorder,
),
)
val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu)
rule.onNodeWithContentDescription(menuContentDescription).performClick()
rule.onNodeWithText("Show only errors").performClick()
val menuContentDescription = activity!!.getString(CommonStrings.a11y_user_menu)
onNodeWithContentDescription(menuContentDescription).performClick()
onNodeWithText("Show only errors").performClick()
eventsRecorder.assertSingle(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = true))
}
@Test
fun `clicking on show only errors sends a PushHistoryEvents(false)`() {
fun `clicking on show only errors sends a PushHistoryEvents(false)`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PushHistoryEvents>()
rule.setPushHistoryView(
setPushHistoryView(
aPushHistoryState(
showOnlyErrors = true,
eventSink = eventsRecorder,
),
)
val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu)
rule.onNodeWithContentDescription(menuContentDescription).performClick()
rule.onNodeWithText("Show only errors").performClick()
val menuContentDescription = activity!!.getString(CommonStrings.a11y_user_menu)
onNodeWithContentDescription(menuContentDescription).performClick()
onNodeWithText("Show only errors").performClick()
eventsRecorder.assertSingle(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = false))
}
@Test
fun `clicking on an invalid event has no effect`() {
fun `clicking on an invalid event has no effect`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PushHistoryEvents>(expectEvents = false)
rule.setPushHistoryView(
setPushHistoryView(
aPushHistoryState(
pushHistoryItems = listOf(
aPushHistoryItem(
@ -93,14 +91,14 @@ class PushHistoryViewTest {
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(A_FORMATTED_DATE).performClick()
onNodeWithText(A_FORMATTED_DATE).performClick()
// No callback invoked
}
@Test
fun `clicking on a valid event emits the expected Event`() {
fun `clicking on a valid event emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PushHistoryEvents>()
rule.setPushHistoryView(
setPushHistoryView(
aPushHistoryState(
pushHistoryItems = listOf(
aPushHistoryItem(
@ -113,7 +111,7 @@ class PushHistoryViewTest {
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(A_FORMATTED_DATE).performClick()
onNodeWithText(A_FORMATTED_DATE).performClick()
eventsRecorder.assertSingle(
PushHistoryEvents.NavigateTo(
sessionId = A_SESSION_ID,
@ -124,7 +122,7 @@ class PushHistoryViewTest {
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setPushHistoryView(
private fun AndroidComposeUiTest<ComponentActivity>.setPushHistoryView(
state: PushHistoryState,
onBackClick: () -> Unit = EnsureNeverCalled(),
) {