Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get Detox to tap on nested text elements that have an onPress?

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!

like image 403
Zidail Avatar asked Jul 20 '18 03:07

Zidail


2 Answers

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>
like image 193
Andrew Avatar answered Oct 26 '22 00:10

Andrew


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.

like image 28
cayleyh Avatar answered Oct 25 '22 23:10

cayleyh