1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package social.grain.support
import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.performScrollToNode
/**
* Assert that [text] reaches the screen, allowing for the fetch behind it.
*
* A plain `assertExists` is a snapshot of one instant, and every one of these
* screens is drawing something a request has to answer first. Waiting is not a
* weaker assertion — it still fails if the text never arrives — but it does not
* fail because a loaded machine took an extra frame.
*
* `waitUntil` rather than a sleep: under Robolectric the recomposer runs on the
* test thread, so blocking it stops the very work being waited for.
*/
fun ComposeTestRule.awaitText(
text: String,
substring: Boolean = false,
timeoutMs: Long = 10_000,
) {
waitUntil(timeoutMs) {
onAllNodes(hasText(text, substring = substring)).fetchSemanticsNodes().isNotEmpty()
}
}
/** As [awaitText], for something named only by its content description. */
fun ComposeTestRule.awaitContentDescription(value: String, timeoutMs: Long = 10_000) {
waitUntil(timeoutMs) {
onAllNodes(hasContentDescription(value)).fetchSemanticsNodes().isNotEmpty()
}
}
/**
* Scroll whatever list is on screen until [text] is composed, then assert it.
*
* A lazy list never composes what is below the fold, so `assertExists` on a
* chip four screens down fails for the layout rather than for the thing being
* tested — and `performScrollTo` can't help, because it needs the node to exist
* before it can scroll to it.
*/
fun ComposeTestRule.scrollTo(text: String) {
val target = hasText(text)
if (onAllNodes(target).fetchSemanticsNodes().isNotEmpty()) return
// A screen can have several scrollable regions — a photo strip beside the
// form, a chip row inside it — so try each rather than assuming which one
// holds the row being looked for.
val scrollers = onAllNodes(androidx.compose.ui.test.hasScrollAction()).fetchSemanticsNodes()
for (index in scrollers.indices) {
runCatching {
onAllNodes(androidx.compose.ui.test.hasScrollAction())[index]
.performScrollToNode(target)
}
if (onAllNodes(target).fetchSemanticsNodes().isNotEmpty()) return
}
onNode(target).assertExists()
}