Allow custom TextInput wrapper to support UI testing `performTextInput`

Allow custom TextInput wrapper to support UI testing `performTextInput`

  

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("[email protected]")

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("[email protected]")

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)
    }
}
© 2024 Dagalaxy. All rights reserved.