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,13 +6,16 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.login.impl.screens.chooseaccountprovider
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.login.impl.accountprovider.anAccountProvider
import io.element.android.libraries.architecture.AsyncData
@ -25,36 +28,31 @@ 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 ChooseAccountProviderViewTest {
@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 eventSink = EventsRecorder<ChooseAccountProviderEvents>(expectEvents = false)
ensureCalledOnce {
rule.setChooseAccountProviderView(
setChooseAccountProviderView(
state = aChooseAccountProviderState(
eventSink = eventSink,
),
onBackClick = it,
)
rule.pressBack()
pressBack()
}
}
@Config(qualifiers = "h1024dp")
@Test
fun `selecting an account provider emits the the expected event`() {
fun `selecting an account provider emits the the expected event`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<ChooseAccountProviderEvents>()
rule.setChooseAccountProviderView(
setChooseAccountProviderView(
state = aChooseAccountProviderState(
accountProviders = listOf(
ChooseAccountProviderPresenterTest.accountProvider1,
@ -64,24 +62,24 @@ class ChooseAccountProviderViewTest {
eventSink = eventSink,
),
)
rule.onNodeWithText(ChooseAccountProviderPresenterTest.accountProvider1.title).performClick()
onNodeWithText(ChooseAccountProviderPresenterTest.accountProvider1.title).performClick()
eventSink.assertSingle(ChooseAccountProviderEvents.SelectAccountProvider(ChooseAccountProviderPresenterTest.accountProvider1))
}
@Test
fun `when error is displayed - closing the dialog emits the expected event`() {
fun `when error is displayed - closing the dialog emits the expected event`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<ChooseAccountProviderEvents>()
rule.setChooseAccountProviderView(
setChooseAccountProviderView(
state = aChooseAccountProviderState(
loginMode = AsyncData.Failure(AN_EXCEPTION),
eventSink = eventSink,
),
)
rule.clickOn(CommonStrings.action_ok)
clickOn(CommonStrings.action_ok)
eventSink.assertSingle(ChooseAccountProviderEvents.ClearError)
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setChooseAccountProviderView(
private fun AndroidComposeUiTest<ComponentActivity>.setChooseAccountProviderView(
state: ChooseAccountProviderState,
onBackClick: () -> Unit = EnsureNeverCalled(),
onOAuthDetails: (OAuthDetails) -> Unit = EnsureNeverCalledWithParam(),

View file

@ -6,20 +6,23 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.login.impl.screens.loginpassword
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.AndroidComposeUiTest
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.matrix.test.A_PASSWORD
import io.element.android.libraries.matrix.test.A_USER_NAME
@ -30,158 +33,154 @@ 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 LoginPasswordViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on back invoke back callback`() {
fun `clicking on back invoke back callback`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<LoginPasswordEvents>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setLoginPasswordView(
setLoginPasswordView(
aLoginPasswordState(
eventSink = eventsRecorder
),
onBackClick = callback,
)
rule.pressBack()
pressBack()
}
}
@Test
fun `changing login invokes the expected event`() {
fun `changing login invokes the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<LoginPasswordEvents>()
rule.setLoginPasswordView(
setLoginPasswordView(
aLoginPasswordState(
eventSink = eventsRecorder,
),
)
val userNameHint = rule.activity.getString(CommonStrings.common_username)
rule.onNodeWithText(userNameHint).performTextInput(A_USER_NAME)
val userNameHint = activity!!.getString(CommonStrings.common_username)
onNodeWithText(userNameHint).performTextInput(A_USER_NAME)
eventsRecorder.assertSingle(
LoginPasswordEvents.SetLogin(A_USER_NAME)
)
}
@Test
fun `changing login removes new lines the expected event`() {
fun `changing login removes new lines the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<LoginPasswordEvents>()
rule.setLoginPasswordView(
setLoginPasswordView(
aLoginPasswordState(
eventSink = eventsRecorder,
),
)
val userNameHint = rule.activity.getString(CommonStrings.common_username)
rule.onNodeWithText(userNameHint).performTextInput("a\nb")
val userNameHint = activity!!.getString(CommonStrings.common_username)
onNodeWithText(userNameHint).performTextInput("a\nb")
eventsRecorder.assertSingle(
LoginPasswordEvents.SetLogin("ab")
)
}
@Test
fun `clearing login invokes the expected event`() {
fun `clearing login invokes the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<LoginPasswordEvents>()
rule.setLoginPasswordView(
setLoginPasswordView(
aLoginPasswordState(
formState = aLoginFormState(A_USER_NAME),
eventSink = eventsRecorder,
),
)
val a11yClear = rule.activity.getString(CommonStrings.action_clear)
rule.onNodeWithContentDescription(a11yClear).performClick()
val a11yClear = activity!!.getString(CommonStrings.action_clear)
onNodeWithContentDescription(a11yClear).performClick()
eventsRecorder.assertSingle(
LoginPasswordEvents.SetLogin("")
)
}
@Test
fun `changing password invokes the expected event`() {
fun `changing password invokes the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<LoginPasswordEvents>()
rule.setLoginPasswordView(
setLoginPasswordView(
aLoginPasswordState(
eventSink = eventsRecorder,
),
)
val userNameHint = rule.activity.getString(CommonStrings.common_password)
rule.onNodeWithText(userNameHint).performTextInput(A_PASSWORD)
val userNameHint = activity!!.getString(CommonStrings.common_password)
onNodeWithText(userNameHint).performTextInput(A_PASSWORD)
eventsRecorder.assertSingle(
LoginPasswordEvents.SetPassword(A_PASSWORD)
)
}
@Test
fun `reveal password makes the password visible`() {
fun `reveal password makes the password visible`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<LoginPasswordEvents>(expectEvents = false)
rule.setLoginPasswordView(
setLoginPasswordView(
aLoginPasswordState(
formState = aLoginFormState(password = A_PASSWORD),
eventSink = eventsRecorder,
),
)
rule.onNodeWithTag(TestTags.loginPassword.value).assert(hasText("••••••••"))
onNodeWithTag(TestTags.loginPassword.value).assert(hasText("••••••••"))
val resources = activity!!.resources
// Show password
val a11yShowPassword = rule.activity.getString(CommonStrings.a11y_show_password)
rule.onNodeWithContentDescription(a11yShowPassword).performClick()
rule.onNodeWithTag(TestTags.loginPassword.value).assert(hasText(A_PASSWORD))
val a11yShowPassword = resources.getString(CommonStrings.a11y_show_password)
onNodeWithContentDescription(a11yShowPassword).performClick()
onNodeWithTag(TestTags.loginPassword.value).assert(hasText(A_PASSWORD))
// Hide password
val a11yHidePassword = rule.activity.getString(CommonStrings.a11y_hide_password)
rule.onNodeWithContentDescription(a11yHidePassword).performClick()
rule.onNodeWithTag(TestTags.loginPassword.value).assert(hasText("••••••••"))
val a11yHidePassword = resources.getString(CommonStrings.a11y_hide_password)
onNodeWithContentDescription(a11yHidePassword).performClick()
onNodeWithTag(TestTags.loginPassword.value).assert(hasText("••••••••"))
}
@Test
fun `when login is empty, continue button is not enabled`() {
fun `when login is empty, continue button is not enabled`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<LoginPasswordEvents>(expectEvents = false)
rule.setLoginPasswordView(
setLoginPasswordView(
aLoginPasswordState(
formState = aLoginFormState(password = A_PASSWORD),
eventSink = eventsRecorder,
),
)
val continueStr = rule.activity.getString(CommonStrings.action_continue)
rule.onNodeWithText(continueStr).assertIsNotEnabled()
val continueStr = activity!!.getString(CommonStrings.action_continue)
onNodeWithText(continueStr).assertIsNotEnabled()
}
@Test
fun `when password is empty, continue button is not enabled`() {
fun `when password is empty, continue button is not enabled`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<LoginPasswordEvents>(expectEvents = false)
rule.setLoginPasswordView(
setLoginPasswordView(
aLoginPasswordState(
formState = aLoginFormState(login = A_USER_NAME),
eventSink = eventsRecorder,
),
)
val continueStr = rule.activity.getString(CommonStrings.action_continue)
rule.onNodeWithText(continueStr).assertIsNotEnabled()
val continueStr = activity!!.getString(CommonStrings.action_continue)
onNodeWithText(continueStr).assertIsNotEnabled()
}
@Config(qualifiers = "h1024dp")
@Test
fun `clicking on Continue sends expected event`() {
fun `clicking on Continue sends expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<LoginPasswordEvents>()
rule.setLoginPasswordView(
setLoginPasswordView(
aLoginPasswordState(
formState = aLoginFormState(login = A_USER_NAME, password = A_PASSWORD),
eventSink = eventsRecorder,
),
)
val continueStr = rule.activity.getString(CommonStrings.action_continue)
rule.onNodeWithText(continueStr).assertIsEnabled()
rule.clickOn(CommonStrings.action_continue)
val continueStr = activity!!.getString(CommonStrings.action_continue)
onNodeWithText(continueStr).assertIsEnabled()
clickOn(CommonStrings.action_continue)
eventsRecorder.assertSingle(
LoginPasswordEvents.Submit
)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setLoginPasswordView(
private fun AndroidComposeUiTest<ComponentActivity>.setLoginPasswordView(
state: LoginPasswordState,
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.features.login.impl.screens.onboarding
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 com.google.testing.junit.testparameterinjector.KotlinTestParameters.namedTestValues
import com.google.testing.junit.testparameterinjector.TestParameter
import io.element.android.features.login.impl.R
@ -29,22 +32,17 @@ 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.RobolectricTestParameterInjector
@RunWith(RobolectricTestParameterInjector::class)
class OnboardingViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `when can create account - clicking on create account calls the expected callback`() {
fun `when can create account - clicking on create account calls the expected callback`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
canCreateAccount = true,
showDeveloperSettings = false,
@ -52,40 +50,40 @@ class OnboardingViewTest {
),
onCreateAccount = callback,
)
rule.clickOn(R.string.screen_onboarding_sign_up)
clickOn(R.string.screen_onboarding_sign_up)
// Developer settings should not be shown
val developerSettingsText = rule.activity.getString(CommonStrings.common_developer_options)
rule.onNodeWithContentDescription(developerSettingsText).assertDoesNotExist()
val developerSettingsText = activity!!.getString(CommonStrings.common_developer_options)
onNodeWithContentDescription(developerSettingsText).assertDoesNotExist()
}
}
@Test
fun `when can go back - clicking on back calls the expected callback`() {
fun `when can go back - clicking on back calls the expected callback`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
isAddingAccount = true,
eventSink = eventSink,
),
onBackClick = callback,
)
rule.pressBack()
pressBack()
}
}
@Test
fun `when can login with QR code - clicking on sign in with QR code calls the expected callback`() {
fun `when can login with QR code - clicking on sign in with QR code calls the expected callback`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
canLoginWithQrCode = true,
eventSink = eventSink,
),
onSignInWithQrCode = callback,
)
rule.clickOn(R.string.screen_onboarding_sign_in_with_qr_code)
clickOn(R.string.screen_onboarding_sign_in_with_qr_code)
}
}
@ -95,10 +93,10 @@ class OnboardingViewTest {
"can search account provider" to false,
"cannot search account provider" to true,
)
) {
) = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>(expectEvents = false)
ensureCalledOnceWithParam(mustChooseAccountProvider) { callback ->
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
canLoginWithQrCode = true,
mustChooseAccountProvider = mustChooseAccountProvider,
@ -106,7 +104,7 @@ class OnboardingViewTest {
),
onSignIn = callback,
)
rule.clickOn(R.string.screen_onboarding_sign_in_manually)
clickOn(R.string.screen_onboarding_sign_in_manually)
}
}
@ -116,10 +114,10 @@ class OnboardingViewTest {
"can search account provider" to false,
"cannot search account provider" to true,
)
) {
) = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>(expectEvents = false)
ensureCalledOnceWithParam(mustChooseAccountProvider) { callback ->
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
canLoginWithQrCode = false,
canCreateAccount = false,
@ -128,89 +126,89 @@ class OnboardingViewTest {
),
onSignIn = callback,
)
rule.clickOn(CommonStrings.action_continue)
clickOn(CommonStrings.action_continue)
}
}
@Test
fun `when sign in to pre defined account provider - clicking on button emits the expected event`() {
fun `when sign in to pre defined account provider - clicking on button emits the expected event`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>()
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
defaultAccountProvider = "element.io",
eventSink = eventSink,
),
)
val buttonText = rule.activity.getString(R.string.screen_onboarding_sign_in_to, "element.io")
rule.onNodeWithText(buttonText).performClick()
val buttonText = activity!!.getString(R.string.screen_onboarding_sign_in_to, "element.io")
onNodeWithText(buttonText).performClick()
eventSink.assertSingle(OnBoardingEvents.OnSignIn("element.io"))
}
@Test
fun `when error is displayed - closing the dialog emits the expected event`() {
fun `when error is displayed - closing the dialog emits the expected event`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>()
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
defaultAccountProvider = "element.io",
loginMode = AsyncData.Failure(AN_EXCEPTION),
eventSink = eventSink,
),
)
rule.clickOn(CommonStrings.action_ok)
clickOn(CommonStrings.action_ok)
eventSink.assertSingle(OnBoardingEvents.ClearError)
}
@Test
fun `clicking on report a problem calls the sign in callback`() {
fun `clicking on report a problem calls the sign in callback`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
canReportBug = true,
eventSink = eventSink,
),
onReportProblem = callback,
)
val text = rule.activity.getString(CommonStrings.common_report_a_problem)
rule.onNodeWithText(text).assertExists()
rule.clickOn(CommonStrings.common_report_a_problem)
val text = activity!!.getString(CommonStrings.common_report_a_problem)
onNodeWithText(text).assertExists()
clickOn(CommonStrings.common_report_a_problem)
}
}
@Test
fun `clicking on settings calls the developer settings callback`() {
fun `clicking on settings calls the developer settings callback`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>(expectEvents = false)
ensureCalledOnce { callback ->
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
showDeveloperSettings = true,
eventSink = eventSink,
),
onDeveloperSettingsClick = callback,
)
val text = rule.activity.getString(CommonStrings.common_developer_options)
rule.onNodeWithContentDescription(text).performClick()
val text = activity!!.getString(CommonStrings.common_developer_options)
onNodeWithContentDescription(text).performClick()
}
}
@Test
fun `cannot report a problem when the feature is disabled`() {
fun `cannot report a problem when the feature is disabled`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>(expectEvents = false)
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
canReportBug = false,
eventSink = eventSink,
),
)
val text = rule.activity.getString(CommonStrings.common_report_a_problem)
rule.onNodeWithText(text).assertDoesNotExist()
val text = activity!!.getString(CommonStrings.common_report_a_problem)
onNodeWithText(text).assertDoesNotExist()
}
@Test
fun `when success PasswordLogin - the expected callback is invoked and the event is received`() {
fun `when success PasswordLogin - the expected callback is invoked and the event is received`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>()
ensureCalledOnce { callback ->
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
loginMode = AsyncData.Success(LoginMode.PasswordLogin),
eventSink = eventSink,
@ -222,11 +220,11 @@ class OnboardingViewTest {
}
@Test
fun `when success Oidc - the expected callback is invoked and the event is received`() {
fun `when success Oidc - the expected callback is invoked and the event is received`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>()
val oAuthDetails = OAuthDetails("aUrl")
ensureCalledOnceWithParam(oAuthDetails) { callback ->
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
loginMode = AsyncData.Success(LoginMode.OAuth(oAuthDetails)),
eventSink = eventSink,
@ -238,11 +236,11 @@ class OnboardingViewTest {
}
@Test
fun `when success AccountCreation - the expected callback is invoked and the event is received`() {
fun `when success AccountCreation - the expected callback is invoked and the event is received`() = runAndroidComposeUiTest {
val eventSink = EventsRecorder<OnBoardingEvents>()
val oAuthDetails = OAuthDetails("aUrl")
ensureCalledOnceWithParam(oAuthDetails.url) { callback ->
rule.setOnboardingView(
setOnboardingView(
state = anOnBoardingState(
loginMode = AsyncData.Success(LoginMode.AccountCreation("aUrl")),
eventSink = eventSink,
@ -253,7 +251,7 @@ class OnboardingViewTest {
eventSink.assertSingle(OnBoardingEvents.ClearError)
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setOnboardingView(
private fun AndroidComposeUiTest<ComponentActivity>.setOnboardingView(
state: OnBoardingState,
onBackClick: () -> Unit = EnsureNeverCalled(),
onDeveloperSettingsClick: () -> Unit = EnsureNeverCalled(),

View file

@ -6,49 +6,47 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.login.impl.screens.qrcode.confirmation
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.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.pressBackKey
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class QrCodeConfirmationViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `on back pressed - calls the expected callback`() {
fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setQrCodeConfirmationView(
setQrCodeConfirmationView(
step = QrCodeConfirmationStep.DisplayCheckCode("12"),
onCancel = callback
)
rule.pressBackKey()
pressBackKey()
}
}
@Test
fun `on Cancel button clicked - calls the expected callback`() {
fun `on Cancel button clicked - calls the expected callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setQrCodeConfirmationView(
setQrCodeConfirmationView(
step = QrCodeConfirmationStep.DisplayVerificationCode("123456"),
onCancel = callback
)
rule.clickOn(CommonStrings.action_cancel)
clickOn(CommonStrings.action_cancel)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setQrCodeConfirmationView(
private fun AndroidComposeUiTest<ComponentActivity>.setQrCodeConfirmationView(
step: QrCodeConfirmationStep,
onCancel: () -> Unit
) {

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.login.impl.screens.qrcode.error
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.features.login.impl.qrcode.QrCodeErrorScreenType
import io.element.android.libraries.ui.strings.CommonStrings
@ -18,47 +21,42 @@ import io.element.android.tests.testutils.EnsureNeverCalled
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.pressBackKey
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class QrCodeErrorViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `on back pressed - calls the onCancel callback`() {
fun `on back pressed - calls the onCancel callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setQrCodeErrorView(
setQrCodeErrorView(
onCancel = callback,
)
rule.pressBackKey()
pressBackKey()
}
}
@Test
fun `on try again button clicked - calls the expected callback`() {
fun `on try again button clicked - calls the expected callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setQrCodeErrorView(
setQrCodeErrorView(
onRetry = callback,
)
rule.clickOn(CommonStrings.action_try_again)
clickOn(CommonStrings.action_try_again)
}
}
@Test
fun `on cancel button clicked - calls the expected callback`() {
fun `on cancel button clicked - calls the expected callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setQrCodeErrorView(
setQrCodeErrorView(
onCancel = callback,
)
rule.clickOn(CommonStrings.action_cancel)
clickOn(CommonStrings.action_cancel)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setQrCodeErrorView(
private fun AndroidComposeUiTest<ComponentActivity>.setQrCodeErrorView(
onRetry: () -> Unit = EnsureNeverCalled(),
onCancel: () -> Unit = EnsureNeverCalled(),
errorScreenType: QrCodeErrorScreenType = QrCodeErrorScreenType.UnknownError,

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.login.impl.screens.qrcode.intro
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.features.login.impl.R
import io.element.android.tests.testutils.EnsureNeverCalled
@ -19,42 +22,37 @@ import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.pressBack
import io.element.android.tests.testutils.pressBackKey
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class QrCodeIntroViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `on back pressed - calls the expected callback`() {
fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setQrCodeIntroView(
setQrCodeIntroView(
state = aQrCodeIntroState(),
onBackClicked = callback
)
rule.pressBackKey()
pressBackKey()
}
}
@Test
fun `on back button clicked - calls the expected callback`() {
fun `on back button clicked - calls the expected callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setQrCodeIntroView(
setQrCodeIntroView(
state = aQrCodeIntroState(),
onBackClicked = callback
)
rule.pressBack()
pressBack()
}
}
@Test
fun `when can continue - calls the expected callback`() {
fun `when can continue - calls the expected callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setQrCodeIntroView(
setQrCodeIntroView(
state = aQrCodeIntroState(canContinue = true),
onContinue = callback
)
@ -62,16 +60,16 @@ class QrCodeIntroViewTest {
}
@Test
fun `on submit button clicked - emits the Continue event`() {
fun `on submit button clicked - emits the Continue event`() = runAndroidComposeUiTest {
val eventRecorder = EventsRecorder<QrCodeIntroEvents>()
rule.setQrCodeIntroView(
setQrCodeIntroView(
state = aQrCodeIntroState(eventSink = eventRecorder),
)
rule.clickOn(R.string.screen_qr_code_login_initial_state_button_title)
clickOn(R.string.screen_qr_code_login_initial_state_button_title)
eventRecorder.assertSingle(QrCodeIntroEvents.Continue)
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setQrCodeIntroView(
private fun AndroidComposeUiTest<ComponentActivity>.setQrCodeIntroView(
state: QrCodeIntroState,
onBackClicked: () -> Unit = EnsureNeverCalled(),
onContinue: () -> 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.features.login.impl.screens.qrcode.scan
import androidx.activity.ComponentActivity
import androidx.camera.lifecycle.ProcessCameraProvider
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 androidx.test.platform.app.InstrumentationRegistry
import io.element.android.libraries.architecture.AsyncAction
@ -24,16 +27,11 @@ import io.element.android.tests.testutils.ensureCalledOnceWithParam
import io.element.android.tests.testutils.pressBackKey
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class QrCodeScanViewTest {
@get:Rule
val rule = createAndroidComposeRule<ComponentActivity>()
private var provider: ProcessCameraProvider? = null
@Before
@ -48,28 +46,28 @@ class QrCodeScanViewTest {
}
@Test
fun `on back pressed - calls the expected callback`() {
fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest {
ensureCalledOnce { callback ->
rule.setQrCodeScanView(
setQrCodeScanView(
state = aQrCodeScanState(),
onBackClick = callback
)
rule.pressBackKey()
pressBackKey()
}
}
@Test
fun `on QR code data ready - calls the expected callback`() {
fun `on QR code data ready - calls the expected callback`() = runAndroidComposeUiTest {
val data = FakeMatrixQrCodeLoginData()
ensureCalledOnceWithParam<MatrixQrCodeLoginData>(data) { callback ->
rule.setQrCodeScanView(
setQrCodeScanView(
state = aQrCodeScanState(authenticationAction = AsyncAction.Success(data)),
onQrCodeDataReady = callback
)
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setQrCodeScanView(
private fun AndroidComposeUiTest<ComponentActivity>.setQrCodeScanView(
state: QrCodeScanState,
onBackClick: () -> Unit = EnsureNeverCalled(),
onQrCodeDataReady: (MatrixQrCodeLoginData) -> Unit = EnsureNeverCalledWithParam(),