You can define a ViewAction that loops the main thread every 50 milliseconds (or a different time of your choosing) until either the visibility of the View changes to View.GONE or a maximum amount of time elapses.
Follow the steps below to achieve this.
Step 1
Define the ViewAction, as follows:
/**
* A [ViewAction] that waits up to [timeout] milliseconds for a [View]'s visibility value to change to [View.GONE].
*/
class WaitUntilGoneAction(private val timeout: Long) : ViewAction {
override fun getConstraints(): Matcher<View> {
return any(View::class.java)
}
override fun getDescription(): String {
return "wait up to $timeout milliseconds for the view to be gone"
}
override fun perform(uiController: UiController, view: View) {
val endTime = System.currentTimeMillis() + timeout
do {
if (view.visibility == View.GONE) return
uiController.loopMainThreadForAtLeast(50)
} while (System.currentTimeMillis() < endTime)
throw PerformException.Builder()
.withActionDescription(description)
.withCause(TimeoutException("Waited $timeout milliseconds"))
.withViewDescription(HumanReadables.describe(view))
.build()
}
}
Step 2
Define a function that creates an instance of this ViewAction when called, as follows:
/**
* @return a [WaitUntilGoneAction] instance created with the given [timeout] parameter.
*/
fun waitUntilGone(timeout: Long): ViewAction {
return WaitUntilGoneAction(timeout)
}
Step 3
Call on this ViewAction in your test method, as follows:
onView(withId(R.id.loadingTextView)).perform(waitUntilGone(3000L))
Next steps
Run with this concept and similarly create a WaitForTextAction
class that waits until a TextView's text changes to a certain value. In this case, however, you'll probably want to change the Matcher returned by the getConstraints() function from any(View::class.java)
to any(TextView::class.java)
.