Skip to content

Instantly share code, notes, and snippets.

@MeNiks
Last active February 16, 2022 07:24
Show Gist options
  • Save MeNiks/fa9efd83ca6f5f873a8ea8ba552fc0f1 to your computer and use it in GitHub Desktop.
Save MeNiks/fa9efd83ca6f5f873a8ea8ba552fc0f1 to your computer and use it in GitHub Desktop.
Espresso UI Testing Extensions
package com.yourpackagename
import android.os.Environment
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.test.InstrumentationRegistry.getTargetContext
import androidx.test.espresso.Espresso
import androidx.test.espresso.PerformException
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.BoundedMatcher
import androidx.test.espresso.matcher.RootMatchers
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.uiautomator.UiDevice
import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout
import com.mindtickle.android.core.utils.ResourceHelper
import java.io.File
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.Matchers
import org.hamcrest.Matchers.not
import org.hamcrest.TypeSafeMatcher
fun getView(id: Int, isDialog: Boolean = false): ViewInteraction {
val viewInteraction = Espresso.onView(ViewMatchers.withId(id))
if (isDialog)
viewInteraction.inRoot(RootMatchers.isDialog())
return viewInteraction
}
fun getView(text: String): ViewInteraction {
return Espresso.onView(ViewMatchers.withText(text))
}
fun getView(parentId: Int, position: Int, childId: Int): ViewInteraction {
val parentMatcher = ViewMatchers.withId(parentId)
return Espresso.onView(
object : TypeSafeMatcher<View?>() {
override fun describeTo(description: Description) {
description.appendText("with $childId child view of type parentMatcher")
}
override fun matchesSafely(view: View?): Boolean {
view ?: return false
if (view.id != childId)
return false
val parentView: View? = view.rootView.findViewById(parentId)
if (parentView !is ViewGroup) {
return false
}
return parentMatcher.matches(parentView) && parentView.getChildAt(position).findViewById<View>(childId) == view
}
}
)
}
fun ViewInteraction.isVisible(): ViewInteraction {
return check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
}
fun ViewInteraction.isEnabled(): ViewInteraction {
return check(ViewAssertions.matches(ViewMatchers.isEnabled()))
}
fun ViewInteraction.isDisabled(): ViewInteraction {
return check(ViewAssertions.matches(not(ViewMatchers.isEnabled())))
}
fun ViewInteraction.isInvisible(): ViewInteraction {
return check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.INVISIBLE)))
}
fun ViewInteraction.isGone(): ViewInteraction {
return check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.GONE)))
}
fun ViewInteraction.hasString(resourceHelper: ResourceHelper, stringRes: Int): ViewInteraction {
return check(ViewAssertions.matches(ViewMatchers.withText(resourceHelper.getString(stringRes))))
}
fun ViewInteraction.hasString(string: String): ViewInteraction {
return check(ViewAssertions.matches(ViewMatchers.withText(string)))
}
fun ViewInteraction.performClick(): ViewInteraction {
return perform(ViewActions.click())
}
var enableScreenShot = false
fun takeScreenshot(fileName: String, uiDevice: UiDevice) {
if (!enableScreenShot)
return
val filePath = File(Environment.getExternalStorageDirectory().absolutePath + "/Pictures", "$fileName.png")
if (filePath.exists())
filePath.delete()
uiDevice.takeScreenshot(filePath)
}
fun ViewInteraction.selectTabAtPosition(tabIndex: Int) {
perform(
object : ViewAction {
override fun getDescription() = "with tab at index $tabIndex"
override fun getConstraints() = Matchers.allOf(ViewMatchers.isDisplayed(), ViewMatchers.isAssignableFrom(TabLayout::class.java))
override fun perform(uiController: UiController, view: View) {
val tabLayout = view as TabLayout
val tabAtIndex: TabLayout.Tab = tabLayout.getTabAt(tabIndex)
?: throw PerformException.Builder()
.withCause(Throwable("No tab at index $tabIndex"))
.build()
tabAtIndex.select()
}
}
)
}
fun ViewInteraction.hasHint(expectedHint: String): ViewInteraction {
return check(matches(object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("View has hint '$expectedHint'")
}
override fun matchesSafely(item: View?) =
item is TextInputLayout && expectedHint == item.hint || item is TextView && expectedHint == item.hint || item is EditText && expectedHint == item.hint
}))
}
fun ViewInteraction.hasError(expectedError: String?): ViewInteraction {
return check(matches(object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("View has Error '$expectedError'")
}
override fun matchesSafely(item: View?) =
item is TextInputLayout && expectedError == item.error || item is TextView && expectedError == item.error || item is EditText && expectedError == item.error
}))
}
fun ViewInteraction.matchesWithTextColor(color: Int): ViewInteraction? {
return check(matches(object : BoundedMatcher<View?, TextView>(TextView::class.java) {
override fun matchesSafely(textView: TextView): Boolean {
return ContextCompat.getColor(getTargetContext(), color) == textView.currentTextColor
}
override fun describeTo(description: Description) {
description.appendText("with text color: ")
}
}))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment