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

@ -31,7 +31,14 @@ android {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
implementation "androidx.test:core:$androidx_test_version"
implementation"androidx.test.espresso:espresso-core:$espresso_version"
implementation "androidx.test.espresso:espresso-core:$espresso_version"
implementation "androidx.test.espresso:espresso-intents:$espresso_version"
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
implementation "com.google.android.material:material:$androidx_material_version"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:$androidx_swiperefreshlayout_version"
implementation "androidx.core:core-ktx:$androidx_core_version"
}

View file

@ -0,0 +1,17 @@
package org.fnives.test.showcase.android.testutil.intent
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

@ -0,0 +1,44 @@
package org.fnives.test.showcase.android.testutil.snackbar
import android.annotation.SuppressLint
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 as MaterialR
import com.google.android.material.snackbar.Snackbar
import org.hamcrest.Matcher
import org.hamcrest.Matchers
object SnackbarVerificationHelper {
@SuppressLint("RestrictedApi")
fun assertSnackBarIsShownWithText(@StringRes stringResID: Int, doDismiss: Boolean = true) {
Espresso.onView(ViewMatchers.withId(MaterialR.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 assertSnackBarIsNotShown() {
Espresso.onView(ViewMatchers.withId(MaterialR.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

@ -0,0 +1,37 @@
package org.fnives.test.showcase.android.testutil.viewaction.imageview
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 = ContextCompat.getDrawable(context, id)?.apply {
if (tintColor != null) {
setTintList(ColorStateList.valueOf(tintColor))
setTintMode(tintMode)
}
}
return view is ImageView && view.drawable.toBitmap().sameAs(expectedBitmap?.toBitmap())
}
}

View file

@ -0,0 +1,24 @@
package org.fnives.test.showcase.android.testutil.viewaction.progressbar
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

@ -0,0 +1,44 @@
package org.fnives.test.showcase.android.testutil.viewaction.swiperefresh
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

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