Issue#67 Extract ViewActions into Library

This commit is contained in:
Gergely Hegedus 2022-05-27 17:55:38 +03:00
parent a27f19302a
commit 99141c0f17
12 changed files with 46 additions and 40 deletions

View file

@ -10,14 +10,13 @@ import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import org.fnives.test.showcase.R
import org.fnives.test.showcase.testutils.configuration.SnackbarVerificationHelper
import org.fnives.test.showcase.testutils.viewactions.notIntended
import org.fnives.test.showcase.android.testutil.intent.notIntended
import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsNotShown
import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsShownWithText
import org.fnives.test.showcase.ui.home.MainActivity
import org.hamcrest.core.IsNot.not
class RobolectricLoginRobot(
private val snackbarVerificationHelper: SnackbarVerificationHelper = SnackbarVerificationHelper()
) {
class RobolectricLoginRobot {
fun setUsername(username: String): RobolectricLoginRobot = apply {
onView(withId(R.id.user_edit_text))
@ -55,11 +54,11 @@ class RobolectricLoginRobot(
}
fun assertErrorIsShown(@StringRes stringResID: Int) = apply {
snackbarVerificationHelper.assertIsShownWithText(stringResID)
assertSnackBarIsShownWithText(stringResID)
}
fun assertErrorIsNotShown() = apply {
snackbarVerificationHelper.assertIsNotShown()
assertSnackBarIsNotShown()
}
fun assertNavigatedToHome() = apply {

View file

@ -1,42 +0,0 @@
package org.fnives.test.showcase.testutils.configuration
import android.view.View
import androidx.annotation.StringRes
import androidx.test.espresso.Espresso
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers
import com.google.android.material.R
import com.google.android.material.snackbar.Snackbar
import org.hamcrest.Matcher
import org.hamcrest.Matchers
class SnackbarVerificationHelper {
fun assertIsShownWithText(@StringRes stringResID: Int, doDismiss: Boolean = true) {
Espresso.onView(ViewMatchers.withId(R.id.snackbar_text))
.check(ViewAssertions.matches(ViewMatchers.withText(stringResID)))
if (doDismiss) {
Espresso.onView(ViewMatchers.isAssignableFrom(Snackbar.SnackbarLayout::class.java)).perform(ViewActions.swipeRight())
Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainUntilSnackbarDismissed())
}
}
fun assertIsNotShown() {
Espresso.onView(ViewMatchers.withId(R.id.snackbar_text)).check(ViewAssertions.doesNotExist())
}
class LoopMainUntilSnackbarDismissed : ViewAction {
override fun getConstraints(): Matcher<View> = Matchers.isA(View::class.java)
override fun getDescription(): String = "loop MainThread until Snackbar is Dismissed"
override fun perform(uiController: UiController, view: View?) {
while (view?.findViewById<View>(com.google.android.material.R.id.snackbar_text) != null) {
uiController.loopMainThreadForAtLeast(100)
}
}
}
}

View file

@ -1,44 +0,0 @@
package org.fnives.test.showcase.testutils.viewactions
import android.view.View
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.swiperefreshlayout.widget.listener
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import org.fnives.test.showcase.android.testutil.synchronization.runOnUIAwaitOnCurrent
import org.hamcrest.BaseMatcher
import org.hamcrest.CoreMatchers.isA
import org.hamcrest.Description
import org.hamcrest.Matcher
// swipe-refresh-layout swipe-down doesn't work, inspired by https://github.com/robolectric/robolectric/issues/5375
class PullToRefresh : ViewAction {
override fun getConstraints(): Matcher<View> {
return object : BaseMatcher<View>() {
override fun matches(item: Any): Boolean {
return isA(SwipeRefreshLayout::class.java).matches(item)
}
override fun describeMismatch(item: Any, mismatchDescription: Description) {
mismatchDescription.appendText("Expected SwipeRefreshLayout or its Descendant, but got other View")
}
override fun describeTo(description: Description) {
description.appendText("Action SwipeToRefresh to view SwipeRefreshLayout or its descendant")
}
}
}
override fun getDescription(): String {
return "Perform pull-to-refresh on the SwipeRefreshLayout"
}
override fun perform(uiController: UiController, view: View) {
val swipeRefreshLayout = view as SwipeRefreshLayout
runOnUIAwaitOnCurrent {
swipeRefreshLayout.isRefreshing = true
swipeRefreshLayout.listener().onRefresh()
}
}
}

View file

@ -1,24 +0,0 @@
package org.fnives.test.showcase.testutils.viewactions
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.View
import android.widget.ProgressBar
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import org.hamcrest.Matcher
class ReplaceProgressBarDrawableToStatic : ViewAction {
override fun getConstraints(): Matcher<View> =
isAssignableFrom(ProgressBar::class.java)
override fun getDescription(): String =
"replace the ProgressBar drawable"
override fun perform(uiController: UiController, view: View) {
val progressBar: ProgressBar = view as ProgressBar
progressBar.indeterminateDrawable = ColorDrawable(Color.GREEN)
uiController.loopMainThreadUntilIdle()
}
}

View file

@ -1,5 +0,0 @@
@file:Suppress("PackageDirectoryMismatch")
package androidx.swiperefreshlayout.widget
fun SwipeRefreshLayout.listener() = mListener

View file

@ -1,37 +0,0 @@
package org.fnives.test.showcase.testutils.viewactions
import android.content.res.ColorStateList
import android.graphics.PorterDuff
import android.view.View
import android.widget.ImageView
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import org.hamcrest.Description
import org.hamcrest.TypeSafeMatcher
class WithDrawable(
@DrawableRes
private val id: Int,
@ColorRes
private val tint: Int? = null,
private val tintMode: PorterDuff.Mode = PorterDuff.Mode.SRC_IN
) : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("ImageView with drawable same as drawable with id $id")
tint?.let { description.appendText(", tint color id: $tint, mode: $tintMode") }
}
override fun matchesSafely(view: View): Boolean {
val context = view.context
val tintColor = tint?.let { ContextCompat.getColor(view.context, it) }
val expectedBitmap = context.getDrawable(id)?.apply {
if (tintColor != null) {
setTintList(ColorStateList.valueOf(tintColor))
setTintMode(tintMode)
}
}
return view is ImageView && view.drawable.toBitmap().sameAs(expectedBitmap?.toBitmap())
}
}

View file

@ -1,17 +0,0 @@
package org.fnives.test.showcase.testutils.viewactions
import android.content.Intent
import androidx.test.espresso.intent.Intents.intended
import org.hamcrest.Matcher
import org.hamcrest.StringDescription
fun notIntended(matcher: Matcher<Intent>) {
try {
intended(matcher)
} catch (assertionError: AssertionError) {
return
}
val description = StringDescription()
matcher.describeMismatch(null, description)
throw IllegalStateException("Navigate to intent found matching $description")
}

View file

@ -19,11 +19,11 @@ import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.espresso.matcher.ViewMatchers.withText
import org.fnives.test.showcase.R
import org.fnives.test.showcase.android.testutil.intent.notIntended
import org.fnives.test.showcase.android.testutil.viewaction.imageview.WithDrawable
import org.fnives.test.showcase.android.testutil.viewaction.swiperefresh.PullToRefresh
import org.fnives.test.showcase.model.content.Content
import org.fnives.test.showcase.model.content.FavouriteContent
import org.fnives.test.showcase.testutils.viewactions.PullToRefresh
import org.fnives.test.showcase.testutils.viewactions.WithDrawable
import org.fnives.test.showcase.testutils.viewactions.notIntended
import org.fnives.test.showcase.ui.auth.AuthActivity
import org.hamcrest.Matchers.allOf

View file

@ -14,15 +14,14 @@ import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import org.fnives.test.showcase.R
import org.fnives.test.showcase.testutils.configuration.SnackbarVerificationHelper
import org.fnives.test.showcase.testutils.viewactions.ReplaceProgressBarDrawableToStatic
import org.fnives.test.showcase.testutils.viewactions.notIntended
import org.fnives.test.showcase.android.testutil.intent.notIntended
import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsNotShown
import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsShownWithText
import org.fnives.test.showcase.android.testutil.viewaction.progressbar.ReplaceProgressBarDrawableToStatic
import org.fnives.test.showcase.ui.home.MainActivity
import org.hamcrest.core.IsNot.not
class LoginRobot(
private val snackbarVerificationHelper: SnackbarVerificationHelper = SnackbarVerificationHelper()
) {
class LoginRobot {
fun setupIntentResults() {
Intents.intending(hasComponent(MainActivity::class.java.canonicalName))
@ -68,7 +67,7 @@ class LoginRobot(
}
fun assertErrorIsShown(@StringRes stringResID: Int) = apply {
snackbarVerificationHelper.assertIsShownWithText(stringResID)
assertSnackBarIsShownWithText(stringResID)
}
fun assertLoadingBeforeRequests() = apply {
@ -82,7 +81,7 @@ class LoginRobot(
}
fun assertErrorIsNotShown() = apply {
snackbarVerificationHelper.assertIsNotShown()
assertSnackBarIsNotShown()
}
fun assertNavigatedToHome() = apply {

View file

@ -8,14 +8,13 @@ import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.ViewMatchers
import org.fnives.test.showcase.R
import org.fnives.test.showcase.testutils.configuration.SnackbarVerificationHelper
import org.fnives.test.showcase.testutils.viewactions.notIntended
import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsNotShown
import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsShownWithText
import org.fnives.test.showcase.android.testutil.intent.notIntended
import org.fnives.test.showcase.ui.home.MainActivity
import org.hamcrest.core.IsNot
class CodeKataSharedRobotTest(
private val snackbarVerificationHelper: SnackbarVerificationHelper = SnackbarVerificationHelper()
) {
class CodeKataSharedRobotTest {
fun setUsername(username: String): CodeKataSharedRobotTest = apply {
Espresso.onView(ViewMatchers.withId(R.id.user_edit_text))
@ -53,11 +52,11 @@ class CodeKataSharedRobotTest(
}
fun assertErrorIsShown(@StringRes stringResID: Int): CodeKataSharedRobotTest = apply {
snackbarVerificationHelper.assertIsShownWithText(stringResID)
assertSnackBarIsShownWithText(stringResID)
}
fun assertErrorIsNotShown(): CodeKataSharedRobotTest = apply {
snackbarVerificationHelper.assertIsNotShown()
assertSnackBarIsNotShown()
}
fun assertNavigatedToHome(): CodeKataSharedRobotTest = apply {

View file

@ -5,7 +5,7 @@ import android.app.Instrumentation
import android.content.Intent
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers
import org.fnives.test.showcase.testutils.viewactions.notIntended
import org.fnives.test.showcase.android.testutil.intent.notIntended
import org.fnives.test.showcase.ui.auth.AuthActivity
import org.fnives.test.showcase.ui.home.MainActivity