Reputation: 331
Suppose I have 3 @Composable functions: Start, Loading, Result.
In the test, I call the Start function, click the Begin button on it, and the Loading function is called.
The Loading function displays the loading procedure, takes some time, and then calls the Result function.
The Result function renders a field with the text OK.
How to wait in the test for the Result or few seconds function to be drawn and check that the text is rendered OK?
composeTestRule
.onNodeWithText("Begin")
.performClick()
// Here you have to wait ...
composeTestRule
.onNodeWithText("OK")
.assertIsDisplayed()
Upvotes: 17
Views: 11811
Reputation: 6209
I modified per_jansson's reply.
fun ComposeContentTestRule.waitUntilTimeout(
timeoutMillis: Long
) {
AsyncTimer.start(timeoutMillis)
this.waitUntil(
condition = { AsyncTimer.expired },
timeoutMillis = timeoutMillis + 1000
)
}
object AsyncTimer {
var expired = false
fun start(delay: Long = 1000) {
expired = false
val timerTask = TimerTaskImpl {
expired = true
}
Timer().schedule(timerTask, delay)
}
}
class TimerTaskImpl(private val runnable: Runnable) : TimerTask() {
override fun run() {
runnable.run()
}
}
TimerTask
is an abstract class, and when I copied and pasted per_jansson's code, it didn't work for me
Upvotes: 1
Reputation: 2048
Edit: there are new waitUntil functions:
fun waitUntilAtLeastOneExists(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilDoesNotExist(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilExactlyOneExists(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilNodeCount(matcher: SemanticsMatcher, count: Int, timeout: Long = 1000L)
You can use the waitUntil
function, as suggested in the comments:
composeTestRule.waitUntil {
composeTestRule
.onAllNodesWithText("OK")
.fetchSemanticsNodes().size == 1
}
There's a request to improve this API but in the meantime you can get the helpers from this blog post and use it like so:
composeTestRule.waitUntilExists(hasText("OK"))
Upvotes: 22
Reputation: 2189
Based on Pitry's answer I created this extension function:
fun ComposeContentTestRule.waitUntilTimeout(
timeoutMillis: Long
) {
AsyncTimer.start(timeoutMillis)
this.waitUntil(
condition = { AsyncTimer.expired },
timeoutMillis = timeoutMillis + 1000
)
}
object AsyncTimer {
var expired = false
fun start(delay: Long = 1000) {
expired = false
Timer().schedule(delay) {
expired = true
}
}
}
Usage in compose test
composeTestRule.waitUntilTimeout(2000L)
Upvotes: 4
Reputation: 331
So the options are:
Implemented the function like this.
fun asyncTimer (delay: Long = 1000) {
AsyncTimer.start (delay)
composeTestRule.waitUntil (
condition = {AsyncTimer.expired},
timeoutMillis = delay + 1000
)
}
object AsyncTimer {
var expired = false
fun start(delay: Long = 1000){
expired = false
Timer().schedule(delay) {
expired = true
}
}
}
Then I created a base class for the test and starting to write a new test, I inherit and I have the necessary ready-made functionality for tests.
Upvotes: 6