Skip to content

Instantly share code, notes, and snippets.

@Frank1234
Last active November 23, 2019 13:05
Show Gist options
  • Select an option

  • Save Frank1234/1824d6b88fe6901523aaa99673577ca7 to your computer and use it in GitHub Desktop.

Select an option

Save Frank1234/1824d6b88fe6901523aaa99673577ca7 to your computer and use it in GitHub Desktop.

Revisions

  1. Frank1234 revised this gist Jul 2, 2018. 1 changed file with 18 additions and 9 deletions.
    27 changes: 18 additions & 9 deletions EspressoViewFinder.kt
    Original file line number Diff line number Diff line change
    @@ -47,33 +47,42 @@ object EspressoViewFinder {
    }

    override fun getDescription(): String {
    return "waitForDisplayed on viewMatcher <$viewMatcher> with timeOut $timeOut ms."
    return "waitForDisplayed on viewMatcher <$viewMatcher> without timeOut $timeOut ms."
    }

    override fun perform(uiController: UiController, view: View) {

    // wait for idle, so that we don't timeout while waiting on Espresso idling resources:
    uiController.loopMainThreadUntilIdle()

    val found = waitForView(uiController, view)

    if (!found) {
    throw createPerformException(view)
    }
    }

    private fun waitForView(uiController: UiController, view: View): Boolean {

    val timeOutTimeStamp = System.currentTimeMillis() + timeOut
    do {
    // find view with required matcher:
    for (child in TreeIterables.breadthFirstViewTraversal(view)) {
    if (viewMatcher.matches(child) && isDisplayed(child)) {
    return@perform
    return true
    }
    }

    uiController.loopMainThreadForAtLeast(CHECK_INTERVAL)
    } while (System.currentTimeMillis() < timeOutTimeStamp)

    // not found, throw exception
    throw PerformException.Builder()
    .withActionDescription(this.description)
    .withViewDescription(HumanReadables.describe(view))
    .withCause(TimeoutException())
    .build()
    return false
    }

    private fun createPerformException(view: View) = PerformException.Builder()
    .withActionDescription(this.description)
    .withViewDescription(HumanReadables.describe(view))
    .withCause(TimeoutException())
    .build()
    }

    private fun isDisplayed(view: View) = view.getGlobalVisibleRect(Rect()) && withEffectiveVisibility(Visibility.VISIBLE).matches(view)
  2. Frank1234 revised this gist Jul 2, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion EspressoViewFinder.kt
    Original file line number Diff line number Diff line change
    @@ -60,7 +60,7 @@ object EspressoViewFinder {
    // find view with required matcher:
    for (child in TreeIterables.breadthFirstViewTraversal(view)) {
    if (viewMatcher.matches(child) && isDisplayed(child)) {
    return
    return@perform
    }
    }

  3. Frank1234 revised this gist Jun 29, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion EspressoViewFinder.kt
    Original file line number Diff line number Diff line change
    @@ -47,7 +47,7 @@ object EspressoViewFinder {
    }

    override fun getDescription(): String {
    return "waitForDisplayed on viewMatcher <$viewMatcher> without timeOut $timeOut ms."
    return "waitForDisplayed on viewMatcher <$viewMatcher> with timeOut $timeOut ms."
    }

    override fun perform(uiController: UiController, view: View) {
  4. Frank1234 revised this gist Jun 29, 2018. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions EspressoViewFinder.kt
    Original file line number Diff line number Diff line change
    @@ -20,13 +20,13 @@ object EspressoViewFinder {
    private const val TIMEOUT_MS = 10 * 1000L

    /**
    * Waits for the view to become visible, with a timeout of [timeOut]. When it
    * becomes visible, [onDisplayedHandler] will be called.
    * Waits for the view referenced in [viewMatcher] to become visible, with a timeout of [timeOut]. If it
    * becomes visible, [onDisplayedHandler] will be invoked.
    *
    * This method is needed because Espresso idling resources are not sufficient in combination with RN,
    * unless we find a way to also wait on the javascript thread and the bridge.
    * This method is needed because Espresso idling resources are not sufficient in combination with RN;
    * it does not wait on the javascript thread and the bridge.
    *
    * Throws a PerformException with TimeoutException as a cause if view is not displayed within [timeOut].
    * Throws a TimeoutException wrapped in a PerformException when the view is not displayed within [timeOut].
    */
    fun waitForDisplayed(viewMatcher: Matcher<View>,
    timeOut: Long = TIMEOUT_MS,
  5. Frank1234 created this gist Jun 29, 2018.
    100 changes: 100 additions & 0 deletions EspressoViewFinder.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,100 @@
    import android.graphics.Rect
    import android.support.test.espresso.Espresso.onView
    import android.support.test.espresso.PerformException
    import android.support.test.espresso.UiController
    import android.support.test.espresso.ViewAction
    import android.support.test.espresso.matcher.ViewMatchers.*
    import android.support.test.espresso.util.HumanReadables
    import android.support.test.espresso.util.TreeIterables
    import android.view.View
    import android.view.ViewGroup
    import com.facebook.react.uimanager.RootView
    import org.hamcrest.Description
    import org.hamcrest.Matcher
    import org.hamcrest.TypeSafeMatcher
    import java.util.concurrent.TimeoutException

    object EspressoViewFinder {

    private const val CHECK_INTERVAL = 50L
    private const val TIMEOUT_MS = 10 * 1000L

    /**
    * Waits for the view to become visible, with a timeout of [timeOut]. When it
    * becomes visible, [onDisplayedHandler] will be called.
    *
    * This method is needed because Espresso idling resources are not sufficient in combination with RN,
    * unless we find a way to also wait on the javascript thread and the bridge.
    *
    * Throws a PerformException with TimeoutException as a cause if view is not displayed within [timeOut].
    */
    fun waitForDisplayed(viewMatcher: Matcher<View>,
    timeOut: Long = TIMEOUT_MS,
    onDisplayedHandler: ((Matcher<View>) -> Unit)? = null) {

    // wait for view
    onView(isRoot()).perform(createWaitForDisplayedViewAction(viewMatcher, timeOut))

    // call handler
    onDisplayedHandler?.invoke(viewMatcher)
    }

    private fun createWaitForDisplayedViewAction(viewMatcher: Matcher<View>,
    timeOut: Long = TIMEOUT_MS) = object : ViewAction {

    override fun getConstraints(): Matcher<View> {
    return isRoot()
    }

    override fun getDescription(): String {
    return "waitForDisplayed on viewMatcher <$viewMatcher> without timeOut $timeOut ms."
    }

    override fun perform(uiController: UiController, view: View) {

    // wait for idle, so that we don't timeout while waiting on Espresso idling resources:
    uiController.loopMainThreadUntilIdle()

    val timeOutTimeStamp = System.currentTimeMillis() + timeOut
    do {
    // find view with required matcher:
    for (child in TreeIterables.breadthFirstViewTraversal(view)) {
    if (viewMatcher.matches(child) && isDisplayed(child)) {
    return
    }
    }

    uiController.loopMainThreadForAtLeast(CHECK_INTERVAL)
    } while (System.currentTimeMillis() < timeOutTimeStamp)

    // not found, throw exception
    throw PerformException.Builder()
    .withActionDescription(this.description)
    .withViewDescription(HumanReadables.describe(view))
    .withCause(TimeoutException())
    .build()
    }
    }

    private fun isDisplayed(view: View) = view.getGlobalVisibleRect(Rect()) && withEffectiveVisibility(Visibility.VISIBLE).matches(view)

    /**
    * Finds a matcher's view's child at the given index.
    */
    fun childAtIndex(parentMatcher: Matcher<View>, childPosition: Int): Matcher<View> = object : TypeSafeMatcher<View>() {

    override fun describeTo(description: Description) {
    description.appendText("childAtIndex $childPosition of type $parentMatcher")
    }

    override fun matchesSafely(view: View): Boolean {

    if (view.parent !is ViewGroup) {
    return parentMatcher.matches(view.parent)
    }

    val group = view.parent as ViewGroup
    return parentMatcher.matches(view.parent) && group.getChildAt(childPosition) == view
    }
    }
    }