Migrate to v2 testing APIs

This commit is contained in:
Benoit Marty 2026-04-30 15:54:33 +02:00
parent da36323006
commit 11b9efa2c9
83 changed files with 2197 additions and 2320 deletions

View file

@ -6,11 +6,14 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.preferences.impl.about
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.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EnsureNeverCalled
@ -19,51 +22,47 @@ import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.ensureCalledOnceWithParam
import io.element.android.tests.testutils.pressBack
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AboutViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on back invokes back callback`() {
fun `clicking on back invokes back callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setAboutView(
setAboutView(
anAboutState(),
onBackClick = callback,
)
rule.pressBack()
pressBack()
}
}
@Test
fun `clicking on an item invokes the expected callback`() {
fun `clicking on an item invokes the expected callback`() = runAndroidComposeUiTest {
val state = anAboutState()
ensureCalledOnceWithParam(state.elementLegals.first()) { callback ->
rule.setAboutView(
setAboutView(
state,
onElementLegalClick = callback,
)
rule.clickOn(state.elementLegals.first().titleRes)
clickOn(state.elementLegals.first().titleRes)
}
}
@Test
fun `clicking on the open source licenses invokes the expected callback`() {
fun `clicking on the open source licenses invokes the expected callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setAboutView(
setAboutView(
anAboutState(),
onOpenSourceLicensesClick = callback,
)
rule.clickOn(CommonStrings.common_open_source_licenses)
clickOn(CommonStrings.common_open_source_licenses)
}
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setAboutView(
private fun AndroidComposeUiTest<ComponentActivity>.setAboutView(
state: AboutState,
onElementLegalClick: (ElementLegal) -> Unit = EnsureNeverCalledWithParam(),
onOpenSourceLicensesClick: () -> Unit = EnsureNeverCalled(),

View file

@ -6,13 +6,16 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.preferences.impl.advanced
import androidx.activity.ComponentActivity
import androidx.compose.runtime.CompositionLocalProvider
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 com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.Interaction
@ -30,104 +33,99 @@ import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.pressBack
import kotlinx.collections.immutable.toImmutableList
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 AdvancedSettingsViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on back invokes the expected callback`() {
fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>(expectEvents = false)
ensureCalledOnce {
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder
),
onBackClick = it
)
rule.pressBack()
pressBack()
}
}
@Test
fun `clicking on other theme emits the expected event`() {
fun `clicking on other theme emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>()
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.common_appearance)
rule.clickOn(CommonStrings.common_dark)
clickOn(CommonStrings.common_appearance)
clickOn(CommonStrings.common_dark)
eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTheme(ThemeOption.Dark))
}
@Test
fun `black theme is shown when available`() {
rule.setAdvancedSettingsView(
fun `black theme is shown when available`() = runAndroidComposeUiTest {
setAdvancedSettingsView(
state = aAdvancedSettingsState(
availableThemeOptions = ThemeOption.entries.toImmutableList(),
),
)
rule.clickOn(CommonStrings.common_appearance)
rule.run {
val text = activity.getString(CommonStrings.common_black)
clickOn(CommonStrings.common_appearance)
run {
val text = activity!!.getString(CommonStrings.common_black)
onNodeWithText(text).assertExists()
}
}
@Test
fun `black theme is hidden when unavailable`() {
rule.setAdvancedSettingsView(
fun `black theme is hidden when unavailable`() = runAndroidComposeUiTest {
setAdvancedSettingsView(
state = aAdvancedSettingsState(
availableThemeOptions = ThemeOption.entries.filterNot { it == ThemeOption.Black }.toImmutableList(),
),
)
rule.clickOn(CommonStrings.common_appearance)
rule.assertNoNodeWithText(CommonStrings.common_black)
clickOn(CommonStrings.common_appearance)
assertNoNodeWithText(CommonStrings.common_black)
}
@Test
fun `clicking on View source emits the expected event`() {
fun `clicking on View source emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>()
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_view_source)
clickOn(CommonStrings.action_view_source)
eventsRecorder.assertSingle(AdvancedSettingsEvents.SetDeveloperModeEnabled(true))
}
@Test
fun `clicking on Share presence emits the expected event`() {
fun `clicking on Share presence emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>()
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder,
),
)
rule.clickOn(R.string.screen_advanced_settings_share_presence)
clickOn(R.string.screen_advanced_settings_share_presence)
eventsRecorder.assertSingle(AdvancedSettingsEvents.SetSharePresenceEnabled(true))
}
@Test
fun `clicking on media to enable compression emits the expected event`() {
fun `clicking on media to enable compression emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>()
val analyticsService = FakeAnalyticsService()
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder,
),
analyticsService = analyticsService
)
rule.clickOn(R.string.screen_advanced_settings_media_compression_description)
clickOn(R.string.screen_advanced_settings_media_compression_description)
eventsRecorder.assertSingle(AdvancedSettingsEvents.SetCompressMedia(true))
assertThat(analyticsService.capturedEvents).isEqualTo(
listOf(
@ -139,17 +137,17 @@ class AdvancedSettingsViewTest {
}
@Test
fun `clicking on media to disable compression emits the expected event`() {
fun `clicking on media to disable compression emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>()
val analyticsService = FakeAnalyticsService()
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
mediaOptimizationState = MediaOptimizationState.AllMedia(isEnabled = true),
eventSink = eventsRecorder,
),
analyticsService = analyticsService
)
rule.clickOn(R.string.screen_advanced_settings_media_compression_description)
clickOn(R.string.screen_advanced_settings_media_compression_description)
eventsRecorder.assertSingle(AdvancedSettingsEvents.SetCompressMedia(false))
assertThat(analyticsService.capturedEvents).isEqualTo(
listOf(
@ -162,65 +160,65 @@ class AdvancedSettingsViewTest {
@Test
@Config(qualifiers = "h1080dp")
fun `clicking on hide invite avatars emits the expected event`() {
fun `clicking on hide invite avatars emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>()
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder,
hideInviteAvatars = false
),
)
rule.clickOn(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title)
clickOn(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title)
eventsRecorder.assertSingle(AdvancedSettingsEvents.SetHideInviteAvatars(true))
}
@Test
@Config(qualifiers = "h1080dp")
fun `clicking on timeline media preview always hide emits the expected event`() {
fun `clicking on timeline media preview always hide emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>()
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder,
timelineMediaPreviewValue = MediaPreviewValue.On
),
)
rule.clickOn(R.string.screen_advanced_settings_show_media_timeline_always_hide)
clickOn(R.string.screen_advanced_settings_show_media_timeline_always_hide)
eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Off))
}
@Test
@Config(qualifiers = "h1080dp")
fun `clicking on timeline media preview private rooms emits the expected event`() {
fun `clicking on timeline media preview private rooms emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>()
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder,
timelineMediaPreviewValue = MediaPreviewValue.On
),
)
rule.clickOn(R.string.screen_advanced_settings_show_media_timeline_private_rooms)
clickOn(R.string.screen_advanced_settings_show_media_timeline_private_rooms)
eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Private))
}
@Test
@Config(qualifiers = "h1080dp")
fun `clicking on timeline media preview always show emits the expected event`() {
fun `clicking on timeline media preview always show emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>()
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder,
timelineMediaPreviewValue = MediaPreviewValue.Off
),
)
rule.clickOn(R.string.screen_advanced_settings_show_media_timeline_always_show)
clickOn(R.string.screen_advanced_settings_show_media_timeline_always_show)
eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.On))
}
@Test
@Config(qualifiers = "h1080dp")
fun `hide invite avatars toggle is disabled when action is loading`() {
fun `hide invite avatars toggle is disabled when action is loading`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>(expectEvents = false)
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder,
hideInviteAvatars = false,
@ -228,14 +226,14 @@ class AdvancedSettingsViewTest {
),
)
// The toggle should be disabled, so clicking should not emit any events
rule.clickOn(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title)
clickOn(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title)
}
@Test
@Config(qualifiers = "h1080dp")
fun `timeline media preview options are disabled when action is loading`() {
fun `timeline media preview options are disabled when action is loading`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AdvancedSettingsEvents>(expectEvents = false)
rule.setAdvancedSettingsView(
setAdvancedSettingsView(
state = aAdvancedSettingsState(
eventSink = eventsRecorder,
timelineMediaPreviewValue = MediaPreviewValue.On,
@ -243,12 +241,12 @@ class AdvancedSettingsViewTest {
),
)
// The options should be disabled, so clicking should not emit any events
rule.clickOn(R.string.screen_advanced_settings_show_media_timeline_always_hide)
rule.clickOn(R.string.screen_advanced_settings_show_media_timeline_private_rooms)
clickOn(R.string.screen_advanced_settings_show_media_timeline_always_hide)
clickOn(R.string.screen_advanced_settings_show_media_timeline_private_rooms)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setAdvancedSettingsView(
private fun AndroidComposeUiTest<ComponentActivity>.setAdvancedSettingsView(
state: AdvancedSettingsState,
analyticsService: AnalyticsService = FakeAnalyticsService(),
onBackClick: () -> Unit = EnsureNeverCalled(),

View file

@ -6,13 +6,16 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.preferences.impl.blockedusers
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.features.preferences.impl.R
import io.element.android.libraries.architecture.AsyncAction
@ -23,72 +26,67 @@ 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 org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class BlockedUserViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on back invokes back callback`() {
fun `clicking on back invokes back callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<BlockedUsersEvents>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setBlockedUsersView(
setBlockedUsersView(
aBlockedUsersState(
eventSink = eventsRecorder
),
onBackClick = callback,
)
rule.pressBack()
pressBack()
}
}
@Test
fun `clicking on a user emits the expected Event`() {
fun `clicking on a user emits the expected Event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<BlockedUsersEvents>()
val userList = aMatrixUserList()
rule.setBlockedUsersView(
setBlockedUsersView(
aBlockedUsersState(
blockedUsers = userList,
eventSink = eventsRecorder
),
)
rule.onNodeWithText(userList.first().displayName.orEmpty()).performClick()
onNodeWithText(userList.first().displayName.orEmpty()).performClick()
eventsRecorder.assertSingle(BlockedUsersEvents.Unblock(userList.first().userId))
}
@Test
fun `clicking on cancel sends a BlockedUsersEvents`() {
fun `clicking on cancel sends a BlockedUsersEvents`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<BlockedUsersEvents>()
rule.setBlockedUsersView(
setBlockedUsersView(
aBlockedUsersState(
unblockUserAction = AsyncAction.ConfirmingNoParams,
eventSink = eventsRecorder
),
)
rule.clickOn(CommonStrings.action_cancel)
clickOn(CommonStrings.action_cancel)
eventsRecorder.assertSingle(BlockedUsersEvents.Cancel)
}
@Test
fun `clicking on confirm sends a BlockedUsersEvents`() {
fun `clicking on confirm sends a BlockedUsersEvents`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<BlockedUsersEvents>()
rule.setBlockedUsersView(
setBlockedUsersView(
aBlockedUsersState(
unblockUserAction = AsyncAction.ConfirmingNoParams,
eventSink = eventsRecorder
),
)
rule.clickOn(R.string.screen_blocked_users_unblock_alert_action)
clickOn(R.string.screen_blocked_users_unblock_alert_action)
eventsRecorder.assertSingle(BlockedUsersEvents.ConfirmUnblock)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setBlockedUsersView(
private fun AndroidComposeUiTest<ComponentActivity>.setBlockedUsersView(
state: BlockedUsersState,
onBackClick: () -> Unit = EnsureNeverCalled(),
) {

View file

@ -6,13 +6,16 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.preferences.impl.developer
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.features.preferences.impl.R
import io.element.android.tests.testutils.EnsureNeverCalled
@ -20,76 +23,71 @@ 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 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 DeveloperSettingsViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on back invokes the expected callback`() {
fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>(expectEvents = false)
ensureCalledOnce {
rule.setDeveloperSettingsView(
setDeveloperSettingsView(
state = aDeveloperSettingsState(
eventSink = eventsRecorder
),
onBackClick = it
)
rule.pressBack()
pressBack()
}
}
@Config(qualifiers = "h2000dp")
@Test
fun `clicking on push history notification invokes the expected callback`() {
fun `clicking on push history notification invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>(expectEvents = false)
ensureCalledOnce {
rule.setDeveloperSettingsView(
setDeveloperSettingsView(
state = aDeveloperSettingsState(
eventSink = eventsRecorder
),
onPushHistoryClick = it
)
rule.clickOn(R.string.troubleshoot_notifications_entry_point_push_history_title)
clickOn(R.string.troubleshoot_notifications_entry_point_push_history_title)
}
}
@Config(qualifiers = "h2000dp")
@Test
fun `clicking on open showkase invokes the expected callback`() {
fun `clicking on open showkase invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>(expectEvents = false)
ensureCalledOnce {
rule.setDeveloperSettingsView(
setDeveloperSettingsView(
state = aDeveloperSettingsState(
eventSink = eventsRecorder
),
onOpenShowkase = it
)
rule.onNodeWithText("Open Showkase browser").performClick()
onNodeWithText("Open Showkase browser").performClick()
}
}
@Config(qualifiers = "h2200dp")
@Test
fun `clicking on clear cache emits the expected event`() {
fun `clicking on clear cache emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>()
rule.setDeveloperSettingsView(
setDeveloperSettingsView(
state = aDeveloperSettingsState(
eventSink = eventsRecorder
),
)
rule.onNodeWithText("Clear cache").performClick()
onNodeWithText("Clear cache").performClick()
eventsRecorder.assertSingle(DeveloperSettingsEvents.ClearCache)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setDeveloperSettingsView(
private fun AndroidComposeUiTest<ComponentActivity>.setDeveloperSettingsView(
state: DeveloperSettingsState,
onOpenShowkase: () -> Unit = EnsureNeverCalled(),
onPushHistoryClick: () -> Unit = EnsureNeverCalled(),

View file

@ -5,19 +5,22 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.preferences.impl.developer.appsettings
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.AndroidComposeUiTest
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.isDialog
import androidx.compose.ui.test.isEditable
import androidx.compose.ui.test.isFocusable
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.features.preferences.impl.R
import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem
@ -27,78 +30,73 @@ 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 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 AppDeveloperSettingsPageTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on back invokes the expected callback`() {
fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AppDeveloperSettingsEvent>(expectEvents = false)
ensureCalledOnce {
rule.setAppDeveloperSettingsView(
setAppDeveloperSettingsView(
state = anAppDeveloperSettingsState(
eventSink = eventsRecorder
),
onBackClick = it
)
rule.pressBack()
pressBack()
}
}
@Config(qualifiers = "h1500dp")
@Test
fun `clicking on element call url open the dialogs and submit emits the expected event`() {
fun `clicking on element call url open the dialogs and submit emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AppDeveloperSettingsEvent>()
rule.setAppDeveloperSettingsView(
setAppDeveloperSettingsView(
state = anAppDeveloperSettingsState(
eventSink = eventsRecorder
),
)
rule.clickOn(R.string.screen_advanced_settings_element_call_base_url)
val textInputNode = rule.onAllNodes(isEditable().and(isFocusable())).filterToOne(hasAnyAncestor(isDialog()))
clickOn(R.string.screen_advanced_settings_element_call_base_url)
val textInputNode = onAllNodes(isEditable().and(isFocusable())).filterToOne(hasAnyAncestor(isDialog()))
textInputNode.performTextInput("https://call.element.dev")
rule.clickOn(CommonStrings.action_ok)
clickOn(CommonStrings.action_ok)
eventsRecorder.assertSingle(AppDeveloperSettingsEvent.SetCustomElementCallBaseUrl("https://call.element.dev"))
}
@Config(qualifiers = "h2000dp")
@Test
fun `clicking on open showkase invokes the expected callback`() {
fun `clicking on open showkase invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AppDeveloperSettingsEvent>(expectEvents = false)
ensureCalledOnce {
rule.setAppDeveloperSettingsView(
setAppDeveloperSettingsView(
state = anAppDeveloperSettingsState(
eventSink = eventsRecorder
),
onOpenShowkase = it
)
rule.onNodeWithText("Open Showkase browser").performClick()
onNodeWithText("Open Showkase browser").performClick()
}
}
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on log level emits the expected event`() {
fun `clicking on log level emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AppDeveloperSettingsEvent>()
rule.setAppDeveloperSettingsView(
setAppDeveloperSettingsView(
state = anAppDeveloperSettingsState(
eventSink = eventsRecorder
),
)
rule.onNodeWithText("Tracing log level").performClick()
rule.onNodeWithText("Debug").performClick()
onNodeWithText("Tracing log level").performClick()
onNodeWithText("Debug").performClick()
eventsRecorder.assertSingle(AppDeveloperSettingsEvent.SetTracingLogLevel(LogLevelItem.DEBUG))
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setAppDeveloperSettingsView(
private fun AndroidComposeUiTest<ComponentActivity>.setAppDeveloperSettingsView(
state: AppDeveloperSettingsState,
onOpenShowkase: () -> Unit = EnsureNeverCalled(),
onBackClick: () -> Unit = EnsureNeverCalled(),

View file

@ -6,13 +6,16 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.preferences.impl.notifications
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.features.preferences.impl.R
import io.element.android.libraries.architecture.AsyncAction
@ -25,76 +28,71 @@ import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.ensureCalledOnceWithParam
import io.element.android.tests.testutils.pressBack
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
@RunWith(AndroidJUnit4::class)
class NotificationSettingsViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on back invokes the expected callback`() {
fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
ensureCalledOnce {
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aValidNotificationSettingsState(
eventSink = eventsRecorder
),
onBackClick = it
)
rule.pressBack()
pressBack()
}
eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled)
}
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on troubleshoot notification invokes the expected callback`() {
fun `clicking on troubleshoot notification invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
ensureCalledOnce {
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aValidNotificationSettingsState(
eventSink = eventsRecorder
),
onTroubleshootNotificationsClick = it
)
rule.clickOn(R.string.troubleshoot_notifications_entry_point_title)
clickOn(R.string.troubleshoot_notifications_entry_point_title)
}
eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled)
}
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on group chats invokes the expected callback`() {
fun `clicking on group chats invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
ensureCalledOnceWithParam(false) {
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aValidNotificationSettingsState(
eventSink = eventsRecorder
),
onOpenEditDefault = it
)
rule.clickOn(R.string.screen_notification_settings_group_chats)
clickOn(R.string.screen_notification_settings_group_chats)
}
eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled)
}
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on direct chats invokes the expected callback`() {
fun `clicking on direct chats invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
ensureCalledOnceWithParam(true) {
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aValidNotificationSettingsState(
eventSink = eventsRecorder
),
onOpenEditDefault = it
)
rule.clickOn(R.string.screen_notification_settings_direct_chats)
clickOn(R.string.screen_notification_settings_direct_chats)
}
eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled)
}
@ -111,15 +109,15 @@ class NotificationSettingsViewTest {
testNotificationToggle(false)
}
private fun testNotificationToggle(initialState: Boolean) {
private fun testNotificationToggle(initialState: Boolean) = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aValidNotificationSettingsState(
appNotificationEnabled = initialState,
eventSink = eventsRecorder
),
)
rule.clickOn(R.string.screen_notification_settings_enable_notifications)
clickOn(R.string.screen_notification_settings_enable_notifications)
eventsRecorder.assertList(
listOf(
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
@ -140,15 +138,15 @@ class NotificationSettingsViewTest {
testAtRoomToggle(false)
}
private fun testAtRoomToggle(initialState: Boolean) {
private fun testAtRoomToggle(initialState: Boolean) = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aValidNotificationSettingsState(
atRoomNotificationsEnabled = initialState,
eventSink = eventsRecorder
),
)
rule.clickOn(R.string.screen_notification_settings_room_mention_label)
clickOn(R.string.screen_notification_settings_room_mention_label)
eventsRecorder.assertList(
listOf(
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
@ -169,15 +167,15 @@ class NotificationSettingsViewTest {
testInvitationToggle(false)
}
private fun testInvitationToggle(initialState: Boolean) {
private fun testInvitationToggle(initialState: Boolean) = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aValidNotificationSettingsState(
inviteForMeNotificationsEnabled = initialState,
eventSink = eventsRecorder
),
)
rule.clickOn(R.string.screen_notification_settings_invite_for_me_label)
clickOn(R.string.screen_notification_settings_invite_for_me_label)
eventsRecorder.assertList(
listOf(
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
@ -188,15 +186,15 @@ class NotificationSettingsViewTest {
@Config(qualifiers = "h1024dp")
@Test
fun `with an error configuration, clicking on continue emits the expected events`() {
fun `with an error configuration, clicking on continue emits the expected events`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aValidNotificationSettingsState(
changeNotificationSettingAction = AsyncAction.Failure(AN_EXCEPTION),
eventSink = eventsRecorder
),
)
rule.clickOn(CommonStrings.action_ok)
clickOn(CommonStrings.action_ok)
eventsRecorder.assertList(
listOf(
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
@ -207,15 +205,15 @@ class NotificationSettingsViewTest {
@Config(qualifiers = "h1024dp")
@Test
fun `with invalid configuration, clicking on continue emits the expected events`() {
fun `with invalid configuration, clicking on continue emits the expected events`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aInvalidNotificationSettingsState(
fixFailed = false,
eventSink = eventsRecorder
),
)
rule.clickOn(CommonStrings.action_continue)
clickOn(CommonStrings.action_continue)
eventsRecorder.assertList(
listOf(
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
@ -226,15 +224,15 @@ class NotificationSettingsViewTest {
@Config(qualifiers = "h1024dp")
@Test
fun `with invalid configuration and error, clicking on OK emits the expected events`() {
fun `with invalid configuration and error, clicking on OK emits the expected events`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aInvalidNotificationSettingsState(
fixFailed = true,
eventSink = eventsRecorder
),
)
rule.clickOn(CommonStrings.action_ok)
clickOn(CommonStrings.action_ok)
eventsRecorder.assertList(
listOf(
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
@ -245,14 +243,14 @@ class NotificationSettingsViewTest {
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on Push notification provider emits the expected event`() {
fun `clicking on Push notification provider emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aValidNotificationSettingsState(
eventSink = eventsRecorder
),
)
rule.clickOn(R.string.screen_advanced_settings_push_provider_android)
clickOn(R.string.screen_advanced_settings_push_provider_android)
eventsRecorder.assertList(
listOf(
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
@ -262,16 +260,16 @@ class NotificationSettingsViewTest {
}
@Test
fun `clicking on a push provider emits the expected event`() {
fun `clicking on a push provider emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
rule.setNotificationSettingsView(
setNotificationSettingsView(
state = aValidNotificationSettingsState(
eventSink = eventsRecorder,
showChangePushProviderDialog = true,
availablePushDistributors = listOf(aDistributor("P1"), aDistributor("P2"))
),
)
rule.onNodeWithText("P2").performClick()
onNodeWithText("P2").performClick()
eventsRecorder.assertList(
listOf(
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
@ -281,7 +279,7 @@ class NotificationSettingsViewTest {
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setNotificationSettingsView(
private fun AndroidComposeUiTest<ComponentActivity>.setNotificationSettingsView(
state: NotificationSettingsState,
onOpenEditDefault: (isOneToOne: Boolean) -> Unit = EnsureNeverCalledWithParam(),
onTroubleshootNotificationsClick: () -> Unit = EnsureNeverCalled(),

View file

@ -5,13 +5,16 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.preferences.impl.root
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.features.preferences.impl.R
import io.element.android.libraries.matrix.api.user.MatrixUser
@ -25,49 +28,45 @@ import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.ensureCalledOnceWithParam
import io.element.android.tests.testutils.pressBack
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PreferencesRootViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on back invokes back callback`() {
fun `clicking on back invokes back callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
eventSink = eventsRecorder
),
onBackClick = callback,
)
rule.pressBack()
pressBack()
}
}
@Test
fun `click on User profile invokes the expected callback`() {
fun `click on User profile invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
val user = aMatrixUser()
ensureCalledOnceWithParam(user) { callback ->
rule.setView(
setView(
aPreferencesRootState(
myUser = user,
eventSink = eventsRecorder,
),
onOpenUserProfile = callback,
)
rule.onNodeWithText("Alice").performClick()
onNodeWithText("Alice").performClick()
}
}
@Test
fun `clicking on other session sends a SwitchToSession`() {
fun `clicking on other session sends a SwitchToSession`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>()
rule.setView(
setView(
aPreferencesRootState(
isMultiAccountEnabled = true,
otherSessions = listOf(
@ -79,366 +78,366 @@ class PreferencesRootViewTest {
eventSink = eventsRecorder,
),
)
rule.onNodeWithText("Bob").performClick()
onNodeWithText("Bob").performClick()
eventsRecorder.assertSingle(PreferencesRootEvent.SwitchToSession(A_USER_ID_2))
}
@Test
fun `click on Add account invokes the expected callback`() {
fun `click on Add account invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
isMultiAccountEnabled = true,
eventSink = eventsRecorder,
),
onAddAccountClick = callback,
)
rule.clickOn(CommonStrings.common_add_another_account)
clickOn(CommonStrings.common_add_another_account)
}
}
@Test
fun `when multi account is not enabled, item is not shown`() {
fun `when multi account is not enabled, item is not shown`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
rule.setView(
setView(
aPreferencesRootState(
isMultiAccountEnabled = false,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.common_add_another_account)).assertDoesNotExist()
onNodeWithText(activity!!.getString(CommonStrings.common_add_another_account)).assertDoesNotExist()
}
@Test
fun `click on Encryption invokes the expected callback`() {
fun `click on Encryption invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
showSecureBackup = true,
eventSink = eventsRecorder,
),
onSecureBackupClick = callback,
)
rule.clickOn(CommonStrings.common_encryption)
clickOn(CommonStrings.common_encryption)
}
}
@Test
fun `when showSecureBackup is false, item is not shown`() {
fun `when showSecureBackup is false, item is not shown`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
rule.setView(
setView(
aPreferencesRootState(
showSecureBackup = false,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.common_encryption)).assertDoesNotExist()
onNodeWithText(activity!!.getString(CommonStrings.common_encryption)).assertDoesNotExist()
}
@Test
fun `click on Manage account invokes the expected callback`() {
fun `click on Manage account invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnceWithParam("aUrl") { callback ->
rule.setView(
setView(
aPreferencesRootState(
accountManagementUrl = "aUrl",
eventSink = eventsRecorder,
),
onManageAccountClick = callback,
)
rule.clickOn(CommonStrings.action_manage_account_and_devices)
clickOn(CommonStrings.action_manage_account_and_devices)
}
}
@Test
fun `when accountManagementUrl is null, item is not shown`() {
fun `when accountManagementUrl is null, item is not shown`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
rule.setView(
setView(
aPreferencesRootState(
accountManagementUrl = null,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.action_manage_account_and_devices)).assertDoesNotExist()
onNodeWithText(activity!!.getString(CommonStrings.action_manage_account_and_devices)).assertDoesNotExist()
}
@Test
fun `click on Link new devices invokes the expected callback`() {
fun `click on Link new devices invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
showLinkNewDevice = true,
eventSink = eventsRecorder,
),
onLinkNewDeviceClick = callback,
)
rule.clickOn(CommonStrings.common_link_new_device)
clickOn(CommonStrings.common_link_new_device)
}
}
@Test
fun `when showLinkNewDevice is false, item is not shown`() {
fun `when showLinkNewDevice is false, item is not shown`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
rule.setView(
setView(
aPreferencesRootState(
showLinkNewDevice = false,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.common_link_new_device)).assertDoesNotExist()
onNodeWithText(activity!!.getString(CommonStrings.common_link_new_device)).assertDoesNotExist()
}
@Test
fun `click on Analytics invokes the expected callback`() {
fun `click on Analytics invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
showAnalyticsSettings = true,
eventSink = eventsRecorder,
),
onOpenAnalytics = callback,
)
rule.clickOn(CommonStrings.common_analytics)
clickOn(CommonStrings.common_analytics)
}
}
@Test
fun `when showAnalyticsSettings is false, item is not shown`() {
fun `when showAnalyticsSettings is false, item is not shown`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
rule.setView(
setView(
aPreferencesRootState(
showAnalyticsSettings = false,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.common_analytics)).assertDoesNotExist()
onNodeWithText(activity!!.getString(CommonStrings.common_analytics)).assertDoesNotExist()
}
@Test
fun `click on Report a problem invokes the expected callback`() {
fun `click on Report a problem invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
canReportBug = true,
eventSink = eventsRecorder,
),
onOpenRageShake = callback,
)
rule.clickOn(CommonStrings.common_report_a_problem)
clickOn(CommonStrings.common_report_a_problem)
}
}
@Test
fun `when canReportBug is false, item is not shown`() {
fun `when canReportBug is false, item is not shown`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
rule.setView(
setView(
aPreferencesRootState(
canReportBug = false,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.common_report_a_problem)).assertDoesNotExist()
onNodeWithText(activity!!.getString(CommonStrings.common_report_a_problem)).assertDoesNotExist()
}
@Test
fun `click on Screen lock invokes the expected callback`() {
fun `click on Screen lock invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
eventSink = eventsRecorder,
),
onOpenLockScreenSettings = callback,
)
rule.clickOn(CommonStrings.common_screen_lock)
clickOn(CommonStrings.common_screen_lock)
}
}
@Test
fun `click on About invokes the expected callback`() {
fun `click on About invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
eventSink = eventsRecorder,
),
onOpenAbout = callback,
)
rule.clickOn(CommonStrings.common_about)
clickOn(CommonStrings.common_about)
}
}
@Test
fun `click on Developer settings invokes the expected callback`() {
fun `click on Developer settings invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
showDeveloperSettings = true,
eventSink = eventsRecorder,
),
onOpenDeveloperSettings = callback,
)
rule.clickOn(CommonStrings.common_developer_options)
clickOn(CommonStrings.common_developer_options)
}
}
@Test
fun `when showDeveloperSettings is false, item is not shown`() {
fun `when showDeveloperSettings is false, item is not shown`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
rule.setView(
setView(
aPreferencesRootState(
showDeveloperSettings = false,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.common_developer_options)).assertDoesNotExist()
onNodeWithText(activity!!.getString(CommonStrings.common_developer_options)).assertDoesNotExist()
}
@Test
fun `click on Advanced settings invokes the expected callback`() {
fun `click on Advanced settings invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
eventSink = eventsRecorder,
),
onOpenAdvancedSettings = callback,
)
rule.clickOn(CommonStrings.common_advanced_settings)
clickOn(CommonStrings.common_advanced_settings)
}
}
@Test
fun `click on Labs invokes the expected callback`() {
fun `click on Labs invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
showLabsItem = true,
eventSink = eventsRecorder,
),
onOpenLabs = callback,
)
rule.clickOn(R.string.screen_labs_title)
clickOn(R.string.screen_labs_title)
}
}
@Test
fun `when showLabsItem is false, item is not shown`() {
fun `when showLabsItem is false, item is not shown`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
rule.setView(
setView(
aPreferencesRootState(
showLabsItem = false,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(rule.activity.getString(R.string.screen_labs_title)).assertDoesNotExist()
onNodeWithText(activity!!.getString(R.string.screen_labs_title)).assertDoesNotExist()
}
@Test
fun `click on Notification invokes the expected callback`() {
fun `click on Notification invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
eventSink = eventsRecorder,
),
onOpenNotificationSettings = callback,
)
rule.clickOn(R.string.screen_notification_settings_title)
clickOn(R.string.screen_notification_settings_title)
}
}
@Test
fun `click on Blocked users invokes the expected callback`() {
fun `click on Blocked users invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
nbOfBlockedUsers = 1,
eventSink = eventsRecorder,
),
onOpenBlockedUsers = callback,
)
rule.clickOn(CommonStrings.common_blocked_users)
clickOn(CommonStrings.common_blocked_users)
}
}
@Test
fun `when nbOfBlockedUsers is 0, item is not shown`() {
fun `when nbOfBlockedUsers is 0, item is not shown`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
rule.setView(
setView(
aPreferencesRootState(
nbOfBlockedUsers = 0,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.common_blocked_users)).assertDoesNotExist()
onNodeWithText(activity!!.getString(CommonStrings.common_blocked_users)).assertDoesNotExist()
}
@Test
fun `click on Remove this device invokes the expected callback`() {
fun `click on Remove this device invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
eventSink = eventsRecorder,
),
onSignOutClick = callback,
)
rule.clickOn(CommonStrings.action_signout)
clickOn(CommonStrings.action_signout)
}
}
@Test
fun `click on Deactivate invokes the expected callback`() {
fun `click on Deactivate invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setView(
setView(
aPreferencesRootState(
canDeactivateAccount = true,
eventSink = eventsRecorder,
),
onDeactivateClick = callback,
)
rule.clickOn(CommonStrings.action_delete_account)
clickOn(CommonStrings.action_delete_account)
}
}
@Test
fun `when canDeactivateAccount is false, item is not shown`() {
fun `when canDeactivateAccount is false, item is not shown`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<PreferencesRootEvent>(expectEvents = false)
rule.setView(
setView(
aPreferencesRootState(
canDeactivateAccount = false,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(rule.activity.getString(CommonStrings.action_delete_account)).assertDoesNotExist()
onNodeWithText(activity!!.getString(CommonStrings.action_delete_account)).assertDoesNotExist()
}
@Test
fun `clicking on version sends a PreferencesRootEvents`() {
fun `clicking on version sends a PreferencesRootEvents`() = runAndroidComposeUiTest {
val version = "VERSION"
val eventsRecorder = EventsRecorder<PreferencesRootEvent>()
rule.setView(
setView(
aPreferencesRootState(
version = version,
eventSink = eventsRecorder,
),
)
rule.onNodeWithText(version).performClick()
onNodeWithText(version).performClick()
eventsRecorder.assertSingle(PreferencesRootEvent.OnVersionInfoClick)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setView(
private fun AndroidComposeUiTest<ComponentActivity>.setView(
state: PreferencesRootState,
onBackClick: () -> Unit = EnsureNeverCalled(),
onAddAccountClick: () -> 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.features.preferences.impl.user.editprofile
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.architecture.AsyncAction
import io.element.android.libraries.matrix.ui.media.AvatarAction
@ -23,96 +26,93 @@ 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 org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class EditUserProfileViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on back emits the expected event`() {
fun `clicking on back emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<EditUserProfileEvent>()
rule.setEditUserProfileView(
setEditUserProfileView(
aEditUserProfileState(
eventSink = eventsRecorder,
),
)
rule.pressBack()
pressBack()
eventsRecorder.assertSingle(EditUserProfileEvent.Exit)
}
@Test
fun `clicking on save from the exit confirmation dialog emits the expected event`() {
fun `clicking on save from the exit confirmation dialog emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<EditUserProfileEvent>()
rule.setEditUserProfileView(
setEditUserProfileView(
aEditUserProfileState(
saveAction = AsyncAction.ConfirmingCancellation,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_save, inDialog = true)
clickOn(CommonStrings.action_save, inDialog = true)
eventsRecorder.assertSingle(EditUserProfileEvent.Save)
}
@Test
fun `clicking on discard exit emits the expected event`() {
fun `clicking on discard exit emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<EditUserProfileEvent>()
rule.setEditUserProfileView(
setEditUserProfileView(
aEditUserProfileState(
saveAction = AsyncAction.ConfirmingCancellation,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_discard)
clickOn(CommonStrings.action_discard)
eventsRecorder.assertSingle(EditUserProfileEvent.Exit)
}
@Test
fun `clicking on save emits the expected event`() {
fun `clicking on save emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<EditUserProfileEvent>()
rule.setEditUserProfileView(
setEditUserProfileView(
aEditUserProfileState(
saveButtonEnabled = true,
saveAction = AsyncAction.Uninitialized,
eventSink = eventsRecorder,
),
)
rule.clickOn(CommonStrings.action_save)
clickOn(CommonStrings.action_save)
eventsRecorder.assertSingle(EditUserProfileEvent.Save)
}
@Test
fun `clicking on avatar opens the bottom sheet dialog`() {
fun `clicking on avatar opens the bottom sheet dialog`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<EditUserProfileEvent>()
val actions = listOf(
AvatarAction.TakePhoto,
AvatarAction.ChoosePhoto,
AvatarAction.Remove,
)
rule.setEditUserProfileView(
setEditUserProfileView(
aEditUserProfileState(
saveAction = AsyncAction.Uninitialized,
avatarActions = actions,
eventSink = eventsRecorder,
),
)
val contentDescription = rule.activity.getString(CommonStrings.a11y_avatar)
rule.onNodeWithContentDescription(contentDescription).performClick()
val resources = activity!!.resources
val contentDescription = resources.getString(CommonStrings.a11y_avatar)
onNodeWithContentDescription(contentDescription).performClick()
// Assert that the actions are displayed
actions.forEach { action ->
val text = rule.activity.getString(action.titleResId)
rule.onNodeWithText(text).assertExists()
val text = resources.getString(action.titleResId)
onNodeWithText(text).assertExists()
}
}
@Test
fun `success invokes the expected callback`() {
fun `success invokes the expected callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<EditUserProfileEvent>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setEditUserProfileView(
setEditUserProfileView(
aEditUserProfileState(
saveAction = AsyncAction.Success(Unit),
eventSink = eventsRecorder,
@ -123,7 +123,7 @@ class EditUserProfileViewTest {
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setEditUserProfileView(
private fun AndroidComposeUiTest<ComponentActivity>.setEditUserProfileView(
state: EditUserProfileState,
onEditProfileSuccess: () -> Unit = EnsureNeverCalled(),
) {