diff --git a/README.md b/README.md index 985930d..3c3f378 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,10 @@ This module contains End-to-End UI tests for the Accounts Journey, connected to > [!IMPORTANT] > A test account must be configured before running tests. -> Provide credentials by editing [accounts-demo/build.gradle.kts](accounts-demo/build.gradle.kts) using the [EBP Sandbox user credentials](https://backbase.io/ebp-sandbox/user-credentials?experience=retail). +> Provide credentials by editing the global `./gradle/gradle.properties` using the [EBP Sandbox user credentials](https://backbase.io/ebp-sandbox/user-credentials?experience=retail): +- `systemProp.TEST_ACCOUNT_USERNAME="username"` +- `systemProp.TEST_ACCOUNT_PASSWORD="password"` + ```sh ./gradlew accounts-demo:cAT diff --git a/accounts-demo/build.gradle.kts b/accounts-demo/build.gradle.kts index 21be8d1..158ccae 100644 --- a/accounts-demo/build.gradle.kts +++ b/accounts-demo/build.gradle.kts @@ -33,8 +33,10 @@ android { buildTypes { debug { - buildConfigField("String", "TEST_ACCOUNT_USERNAME", "\"ADD USER NAME HERE\"") - buildConfigField("String", "TEST_ACCOUNT_PASSWORD", "\"ADD PASSWORD HERE\"") + val testAccountUserName = System.getProperty("TEST_ACCOUNT_USERNAME") ?: "unknown" + val testAccountPassword = System.getProperty("TEST_ACCOUNT_PASSWORD") ?: "unknown" + buildConfigField("String", "TEST_ACCOUNT_USERNAME", testAccountUserName) + buildConfigField("String", "TEST_ACCOUNT_PASSWORD", testAccountPassword) } release { isMinifyEnabled = false diff --git a/accounts-demo/src/androidTest/java/com/backbase/accounts_demo/AccountsJourneyE2ETest.kt b/accounts-demo/src/androidTest/java/com/backbase/accounts_demo/AccountsJourneyE2ETest.kt index 8bf1099..4921b3a 100644 --- a/accounts-demo/src/androidTest/java/com/backbase/accounts_demo/AccountsJourneyE2ETest.kt +++ b/accounts-demo/src/androidTest/java/com/backbase/accounts_demo/AccountsJourneyE2ETest.kt @@ -1,20 +1,17 @@ package com.backbase.accounts_demo -import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView import androidx.test.espresso.ViewInteraction -import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.contrib.RecyclerViewActions -import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.backbase.accounts_demo.presentation.MainActivity import com.backbase.accounts_journey.R +import com.backbase.android.test_data.click +import com.backbase.android.test_data.clickOnRecyclerViewItem import com.backbase.android.test_data.hasItems import com.backbase.android.test_data.shouldBeDisplayed +import com.backbase.android.test_data.shouldHaveDescendant import com.backbase.android.test_data.shouldMatchText import com.backbase.android.test_data.typeTextInInput import com.backbase.android.test_data.waitForRecyclerViewToHaveItems @@ -72,15 +69,16 @@ class AccountsJourneyE2ETest { @Test fun accountListShouldBeDisplayed() { accountHeader.shouldBeDisplayed() - accountList.shouldBeDisplayed() - accountList.check(hasItems()) + accountList + .shouldBeDisplayed() + .hasItems() } @Test fun successfulSearchAccountsAreDisplayed() { - searchInput.typeTextInInput("Sara") + searchInput.typeTextInInput("John") - accountList.check(matches(hasDescendant(withText("Sara's Current Account 1")))) + accountList.shouldHaveDescendant("John's Current Account 1") } @Test @@ -93,9 +91,7 @@ class AccountsJourneyE2ETest { @Test fun accountDetailShouldBeDisplayed() { - accountList.perform( - RecyclerViewActions.actionOnItemAtPosition(1, click()) - ) + accountList.clickOnRecyclerViewItem(1) accountDetailHeader.waitForView() accountDetailHeader.shouldBeDisplayed() @@ -104,20 +100,20 @@ class AccountsJourneyE2ETest { private fun login() { usernameInput.typeTextInInput(BuildConfig.TEST_ACCOUNT_USERNAME) passwordInput.typeTextInInput(BuildConfig.TEST_ACCOUNT_PASSWORD) - loginButton.perform(click()) + loginButton.click() } private fun setupPasscode() { enterPasscode() // Enter the passcode enterPasscode() // Confirm the passcode - confirmButton.perform(click()) + confirmButton.click() } private fun enterPasscode() { - keyboard1Button.perform(click()) - keyboard4Button.perform(click()) - keyboard7Button.perform(click()) - keyboard8Button.perform(click()) - keyboard9Button.perform(click()) + keyboard1Button.click() + keyboard4Button.click() + keyboard7Button.click() + keyboard8Button.click() + keyboard9Button.click() } } diff --git a/app/src/androidTest/java/com/backbase/golden_sample_app/AccountsJourneyE2ETest.kt b/app/src/androidTest/java/com/backbase/golden_sample_app/AccountsJourneyE2ETest.kt index 32b6ddf..5c26f2f 100644 --- a/app/src/androidTest/java/com/backbase/golden_sample_app/AccountsJourneyE2ETest.kt +++ b/app/src/androidTest/java/com/backbase/golden_sample_app/AccountsJourneyE2ETest.kt @@ -42,8 +42,9 @@ class AccountsJourneyE2ETest : BaseE2ETest() { @Test fun accountListShouldBeDisplayed() { accountHeader.shouldBeDisplayed() - accountList.shouldBeDisplayed() - accountList.check(hasItems()) + accountList + .shouldBeDisplayed() + .hasItems() } @Test diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4e5f278..e20c55c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,6 +65,7 @@ detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-form ## E espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" } +espressoContrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "espresso" } ## G gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" } @@ -171,6 +172,7 @@ test-instrumented = [ "coroutines", "coroutinesTest", "espresso", + "espressoContrib", "junit4-android", "koinTest", "testParameterInjector", diff --git a/test-data/build.gradle.kts b/test-data/build.gradle.kts index 4019137..6c02f6b 100644 --- a/test-data/build.gradle.kts +++ b/test-data/build.gradle.kts @@ -15,4 +15,5 @@ dependencies { implementation(libs.junit.jupiter) implementation(libs.espresso) + implementation(libs.espressoContrib) } diff --git a/test-data/src/main/kotlin/com/backbase/android/test_data/ViewInteractionAssertions.kt b/test-data/src/main/kotlin/com/backbase/android/test_data/ViewInteractionAssertions.kt deleted file mode 100644 index c252855..0000000 --- a/test-data/src/main/kotlin/com/backbase/android/test_data/ViewInteractionAssertions.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.backbase.android.test_data - -import androidx.recyclerview.widget.RecyclerView -import androidx.test.espresso.ViewAssertion - -fun hasItems(): ViewAssertion { - return ViewAssertion { view, noViewFoundException -> - if (noViewFoundException != null) throw noViewFoundException - val recyclerView = view as RecyclerView - val itemCount = recyclerView.adapter?.itemCount ?: 0 - assert(itemCount > 0) { - "RecyclerView has no items. Item count = $itemCount" - } - } -} diff --git a/test-data/src/main/kotlin/com/backbase/android/test_data/ViewInteractionExtensions.kt b/test-data/src/main/kotlin/com/backbase/android/test_data/ViewInteractionExtensions.kt index ee50ee8..985181f 100644 --- a/test-data/src/main/kotlin/com/backbase/android/test_data/ViewInteractionExtensions.kt +++ b/test-data/src/main/kotlin/com/backbase/android/test_data/ViewInteractionExtensions.kt @@ -2,11 +2,15 @@ package com.backbase.android.test_data import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.NoMatchingViewException +import androidx.test.espresso.ViewAssertion import androidx.test.espresso.ViewInteraction import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.action.ViewActions.swipeDown import androidx.test.espresso.action.ViewActions.typeText import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.isClickable import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isEnabled @@ -77,4 +81,30 @@ fun ViewInteraction.typeTextInInput(text: String): ViewInteraction = perform(typeText(text)).perform(closeSoftKeyboard()) fun ViewInteraction.pullToRefresh(): ViewInteraction = - perform(androidx.test.espresso.action.ViewActions.swipeDown()) + perform(swipeDown()) + +fun ViewInteraction.click(): ViewInteraction = + perform(androidx.test.espresso.action.ViewActions.click()) + +fun ViewInteraction.shouldHaveDescendant(text: String): ViewInteraction = + check(matches(hasDescendant(withText(text)))) + +fun ViewInteraction.clickOnRecyclerViewItem(position: Int): ViewInteraction = + perform( + RecyclerViewActions.actionOnItemAtPosition( + position, + androidx.test.espresso.action.ViewActions.click() + ) + ) + +fun ViewInteraction.hasItems(): ViewInteraction { + ViewAssertion { view, noViewFoundException -> + if (noViewFoundException != null) throw noViewFoundException + val recyclerView = view as RecyclerView + val itemCount = recyclerView.adapter?.itemCount ?: 0 + assert(itemCount > 0) { + "RecyclerView has no items. Item count = $itemCount" + } + } + return this +}