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() }