Fixed
Status Update
Comments
iz...@google.com <iz...@google.com>
iz...@google.com <iz...@google.com>
iz...@google.com <iz...@google.com> #2
ar...@gmail.com <ar...@gmail.com> #3
please don't assertExsists in onNodeXXX but return null on failure, to allow something like that:
val node = onNode(hasTag(...)) ?: onNode(hasText(...))
Note, not all missing items are necessarily a test failure.
We have assertExists, if we want to test this.
If onNode would return null, it can be used for both cases, but the current variant cannot do both.
Alternatively you could add another implementation, something like onNodeOptional.
waitUntil (or a variant of it) should return the value from the block.
Currently there is no way to detect if the timeout expired.
It's more usable if the value could be anything or null.
val node = waitUntil { if... return node else null }
You will say, this is offtopic here, but the functions above should have similar behavior.
val node = onNode(hasTag(...)) ?: onNode(hasText(...))
Note, not all missing items are necessarily a test failure.
We have assertExists, if we want to test this.
If onNode would return null, it can be used for both cases, but the current variant cannot do both.
Alternatively you could add another implementation, something like onNodeOptional.
waitUntil (or a variant of it) should return the value from the block.
Currently there is no way to detect if the timeout expired.
It's more usable if the value could be anything or null.
val node = waitUntil { if... return node else null }
You will say, this is offtopic here, but the functions above should have similar behavior.
ar...@gmail.com <ar...@gmail.com> #4
that's the code I use currently:
fun ComposeContentTestRule.onNodeWait(
matcher: SemanticsMatcher,
timeoutMillis: Long = 1_000L
): SemanticsNodeInteraction? {
var node: SemanticsNodeInteraction? = null
try {
this.waitUntil(timeoutMillis) {
val nodes = this.onAllNodes(matcher)
if(nodes.fetchSemanticsNodes().size > 0) {
node = nodes.onFirst()
true
} else
false
}
} catch(e: ComposeTimeoutException) {
Timber.d("----------", "Timeout onNodeWait($matcher, $timeoutMillis)")
return null
}
return node
}
fun ComposeContentTestRule.onNodeWaitOrAssert(
matcher: SemanticsMatcher,
timeoutMillis: Long = 1_000L,
assert: Boolean = false
): SemanticsNodeInteraction {
val node = onNodeWait(matcher, timeoutMillis)
return node ?: throw AssertionError("node with (${matcher.description}) does not exist")
}
it is used like this:
val column = test.onNodeWait(hasTestTag("VerticalItemList.Column"), 10000)
column?.printToLog("column") ?: Timber.d("----------", "ERROR: <show some data to see why it is missing>")
// it would be possible to do some alternative here if we didn't get the node,
// maybe there is a question to be answered, before the list is created
assert(column != null)
column?.let {
[...]
// open menu again
it.onChildAt(0).performTouchInput { longClick(center) }
test.waitForIdle()
it.onChildAt(0).performTouchInput { longClick(center) }
test.waitForIdle()
// open sub-menu "Load"
test.onNodeWaitOrAssert(hasText("Load")).performTouchInput { click(center) }
test.waitForIdle()
// count menu items
val count = test.onAllNodesWithText(selectionName).fetchSemanticsNodes().size
assertEquals("menu entries", count, 1)
fun ComposeContentTestRule.onNodeWait(
matcher: SemanticsMatcher,
timeoutMillis: Long = 1_000L
): SemanticsNodeInteraction? {
var node: SemanticsNodeInteraction? = null
try {
this.waitUntil(timeoutMillis) {
val nodes = this.onAllNodes(matcher)
if(nodes.fetchSemanticsNodes().size > 0) {
node = nodes.onFirst()
true
} else
false
}
} catch(e: ComposeTimeoutException) {
Timber.d("----------", "Timeout onNodeWait($matcher, $timeoutMillis)")
return null
}
return node
}
fun ComposeContentTestRule.onNodeWaitOrAssert(
matcher: SemanticsMatcher,
timeoutMillis: Long = 1_000L,
assert: Boolean = false
): SemanticsNodeInteraction {
val node = onNodeWait(matcher, timeoutMillis)
return node ?: throw AssertionError("node with (${matcher.description}) does not exist")
}
it is used like this:
val column = test.onNodeWait(hasTestTag("VerticalItemList.Column"), 10000)
column?.printToLog("column") ?: Timber.d("----------", "ERROR: <show some data to see why it is missing>")
// it would be possible to do some alternative here if we didn't get the node,
// maybe there is a question to be answered, before the list is created
assert(column != null)
column?.let {
[...]
// open menu again
it.onChildAt(0).performTouchInput { longClick(center) }
test.waitForIdle()
it.onChildAt(0).performTouchInput { longClick(center) }
test.waitForIdle()
// open sub-menu "Load"
test.onNodeWaitOrAssert(hasText("Load")).performTouchInput { click(center) }
test.waitForIdle()
// count menu items
val count = test.onAllNodesWithText(selectionName).fetchSemanticsNodes().size
assertEquals("menu entries", count, 1)
ap...@google.com <ap...@google.com> #5
Project: platform/frameworks/support
Branch: androidx-main
commit 2118900c6c4b44749cb6ac30bfb71d8b13fbc665
Author: Izer Onadim <izeronadim@google.com>
Date: Mon Jun 27 12:55:50 2022
Improve rule.waitUntil API to accept a matcher
Extend the `ComposeTestRule.waitUntil` API to accept a matcher and
any count of nodes by adding experimental `waitUntilNodeCount`. Add
some syntactic sugar with `waitUntilAtLeastOneExists`,
`waitUntilExactlyOneExists` and `waitUntilDoesNotExist`.
Bug: 226934294
Test: ./gradlew compose:ui:ui-test-junit4:test
&& ./gradlew compose:ui:ui-test-junit4:cC
Relnote: "Added `waitUntilNodeCount`, `waitUntilAtLeastOneExists`,
`waitUntilExactlyOneExists` and `waitUntilDoesNotExist` as
experimental API to `ComposeTestRule`, extending the `waitUntil` API
to accept any matcher and any count of nodes. See `ComposeTestRule`
for further documentation."
Change-Id: Ifa1b98cf869a78b79f082c4ed8df6c9abd302a87
M compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
A compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeTestRuleWaitUntilTest.kt
A compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/WaitUntilNodeCountTest.kt
M compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.android.kt
M compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/ComposeUiTest.kt
M compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.desktop.kt
M compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.jvm.kt
https://android-review.googlesource.com/2136682
Branch: androidx-main
commit 2118900c6c4b44749cb6ac30bfb71d8b13fbc665
Author: Izer Onadim <izeronadim@google.com>
Date: Mon Jun 27 12:55:50 2022
Improve rule.waitUntil API to accept a matcher
Extend the `ComposeTestRule.waitUntil` API to accept a matcher and
any count of nodes by adding experimental `waitUntilNodeCount`. Add
some syntactic sugar with `waitUntilAtLeastOneExists`,
`waitUntilExactlyOneExists` and `waitUntilDoesNotExist`.
Bug: 226934294
Test: ./gradlew compose:ui:ui-test-junit4:test
&& ./gradlew compose:ui:ui-test-junit4:cC
Relnote: "Added `waitUntilNodeCount`, `waitUntilAtLeastOneExists`,
`waitUntilExactlyOneExists` and `waitUntilDoesNotExist` as
experimental API to `ComposeTestRule`, extending the `waitUntil` API
to accept any matcher and any count of nodes. See `ComposeTestRule`
for further documentation."
Change-Id: Ifa1b98cf869a78b79f082c4ed8df6c9abd302a87
M compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
A compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeTestRuleWaitUntilTest.kt
A compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/WaitUntilNodeCountTest.kt
M compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/AndroidComposeTestRule.android.kt
M compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/ComposeUiTest.kt
M compose/ui/ui-test-junit4/src/desktopMain/kotlin/androidx/compose/ui/test/junit4/DesktopComposeTestRule.desktop.kt
M compose/ui/ui-test-junit4/src/jvmMain/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule.jvm.kt
Description
Feature request for improved waitUntil APIs.
Currently, ComposeTestRule's waitUntil function takes a condition lambda that produces a boolean.
This can be used to very effectively and easily synchronize a test with a UI by adding statements such as:
Making this functionality generic to accept any matcher and any count of nodes would be easy with:
Plus some sugar:
Usage:
More details and options in (internal doc): go/compose-test-waituntil-proposal