ynsok
12/16/2025, 1:33 PM@Test
fun combinedClickableTest() {
var counter = 0
val onCLick: () -> Unit = { ++counter }
val tag = "ButtonTag"
composeTestRule.setContent {
BasicText(
text = "Button",
modifier = Modifier
.testTag(tag)
.combinedClickable(
onClick = {
onCLick()
},
onDoubleClick = {
}
))
}
composeTestRule.onNodeWithTag(tag).performClick() <------ It is not working
composeTestRule.onNodeWithTag(tag).performSemanticsAction(SemanticsActions.OnClick) <------- It is working
composeTestRule.onNodeWithTag(tag)
.assertIsDisplayed()
.assertHasClickAction()
composeTestRule.runOnIdle { assertEquals(1, counter) }
}
Why does the test that executes 'performClick' fail, while the test that executes 'performSemanticsAction' succeeds?Alex Vanyo
12/16/2025, 8:11 PMonDoubleClick, the combinedClickable internally needs to disambiguate between whether the user is performing a click, or if they are performing a double click.
When performClick() is called the inputs that are dispatched advance through time just enough to perform a single click - but at that point, combinedClickable still doesn't know if there is another click coming afterwards (to trigger the double click) or if that's all the input that it is going to see. To figure that out, more time needs to pass.
If you call composeTestRule.mainClock.advanceTimeBy(1000) after performClick(), that will force enough time to pass to trigger the onClick call since it wasn't a double click. It is a bit confusing why runOnIdle doesn't do that.
The exact value to wait for is defined from the platform: LocalViewConfiguration.current.doubleTapTimeoutMillis
The performSemanticsAction bypasses the input disambiguation and immediately executes the OnClick action, so that's why that one works directly.ynsok
12/16/2025, 8:31 PMperformClick(), but on the other hand, such tests are more susceptible to being flaky.Alex Vanyo
12/16/2025, 10:24 PMadvanceTimeBy right now.
What I feel like should be possible (but doesn't work right now) is defining something like this:
fun SemanticsNodeInteraction.performSingleClick(): SemanticsNodeInteraction =
performTouchInput {
click()
advanceEventTime(viewConfiguration.doubleTapTimeoutMillis) // advance time so that we go past the double click timeout
}
But that doesn't work right now, since the advanceEventTime will only call through to advanceTimeBy if another event is sent. And you explicitly don't want another event sent 🙁ynsok
12/17/2025, 9:20 AMLouis Pullen-Freilich [G]
12/17/2025, 1:48 PMLouis Pullen-Freilich [G]
12/17/2025, 1:49 PMynsok
12/17/2025, 7:46 PM