I seem to be having some issues with detox detecting nested elements. I've created an inline link and need to click on it to have my tests progress.
Sample Code:
<Text>
This is the outer text
<Text
onPress={() => {}}
testID="clickable"
>
This is a clickable text component
</Text>
</Text>
How it looks:
This is the outer text This is a clickable text component
Failing test:
await expect(element(by.id('clickable'))).toBeVisible();
Now my issue is that the testID
can not be detected and because of that clickable
can not have .tap()
appled to it.
I know that a possible solution is that we can wrap the nested text
component in a view
but that would require me to know exactly what the width
and height
are since an error is always displayed saying something like views nested within a <Text> must have a width and height
. This is not possible since I have translations and the width
and height
might always be different.
I have also tried just straight up assigning the .tap()
action to the outer text
component but due to the length of the string, the action does not even come close to touching the testID
.
Upon closer inspection of the native iOS elements using Xcode, it seems like the testID
is not assigned to that particular section, and the whole component is just one giant string which could possibly explain why the testID
could not be found.
Any help on this issue would be much appreciated. Thanks!
As you have already noted, the testID is not propagated through for the substring. So the testID won't appear in the view hierarchy and as a result Detox won't find it.
The first thought could be to wrap the text that you want to be tappable in TouchableOpacity (or similar) but that will just result in an invariant violation as the nesting of within is not currently supported by React-Native.
An alternative could be to use views to position your Texts, while removing the nested Texts so that the testID can be found in the view hierarchy. You could achieve a similar result to what you want by doing the following:
<View style={{flexDirection: 'row'}}>
<Text>This is the outer text </Text>
<Text onPress={() => {}} testID="clickable">
This is a clickable text component
</Text>
</View>
I've also solved this issue in certain cases by selecting the parent text instead and using methods like tapAtPoint()
instead of tap()
:
// JSX
<Text testID="login">
<Text>Already have account?</Text>
<Text onPress={}>Login</Text>
</Text>
// Test
await element(by.id('login')).tapAtPoint({ x: 150, y: 50 });
It's more brittle than I'd normally like, but it does work ok for cases where you don't have the component that renders consistent bounds on different device screen sizes. Plus is that is allows you to use the "React-recommended" way to do text appearance or behaviour customization, so your JSX will look more consistent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With