Allow custom TextInput wrapper to support UI testing `performTextInput`

Allow custom TextInput wrapper to support UI testing `performTextInput`
android
Ethan Jackson

I have this wrapper MyInput over compose's builtin TextField that does a series of UI operations and declares a style globally for all my inputs in the app.

I put all my components in a ui module and then I consume it from my different screens.

The problem is when I try to do UI testing and execute performTextInput("[email protected]") like so:

composeTestRule.onNodeWithTag("enter_email_field") .assertIsDisplayed() .performTextInput("

and I get the following result:

java.lang.AssertionError: Failed to perform text input. Failed to assert the following: (SetText is defined) Semantics of the node: Node #20 at (l=84.0, t=1500.0, r=1356.0, b=1699.0)px, Tag: 'enter_email_field' Has 1 child, 10 siblings Selector used: (TestTag = 'enter_email_field')

Would it be possible to have my custom MyInput support these semantics? I tried reading the source for TextField but I fail to see where this is declared?

if i declare the semantics for setText myself i am asked for requestFocus and then insertTextAtCursor? Id like to just inherit these from the inner BasicTextField

Answer

If your custom TextInput wrapper has TextField as a child of some other composable you should first get the child TextField and then perform text input on it.

composeTestRule.onNodeWithTag("enter_email_field") .onChildren() .filterToOne(hasSetTextAction()) .performTextInput("

You can create a helper function to avoid repetition.

fun SemanticsNodeInteraction.editTextNode() = onChildren().filterToOne(hasSetTextAction())

Previous Answer

If you want your custom text field to support performTextInput; make sure you are supplying the test tag on the inner TextField's Modifier.

This will work

@Composable fun MyTextField( modifier: Modifier, testTag: String, ) { var text by remember { mutableStateOf("Initial") } Box(modifier) { TextField(text, { text = it }, Modifier.testTag(testTag)) } }

This won't

@Composable fun MyTextField( modifier: Modifier, testTag: String, ) { var text by remember { mutableStateOf("Initial") } Box(modifier.testTag(testTag)) { TextField(text, { text = it }, Modifier) } }

Related Articles